Solved

Function Objects

Posted on 2004-08-20
6
249 Views
Last Modified: 2013-12-14

In my quest to understand function objects, portions of the code below is a mystery to me.  So now:

# include <iostream>
# include <ostream>
# include <string>
# include <sstream>
# include <vector>
# include <algorithm>

struct Split
 {
  std::string Buffer;
  std::vector<unsigned char> Vec;

  Split( std::string Src ) : Buffer( Src )
   {
    std::istringstream Iss( Buffer );

    while( Iss.good() && std::getline( Iss, Buffer, '.' ) )
           Vec.push_back( std::strtoul( Buffer.c_str(), 0, 10 ) );
   }

  void operator() ( char ) const {}

  operator std::vector<unsigned char>() const { return Vec; }
 };

int main()
 {
  std::string Src( "1.3.6.1.5.1.11.3.2.1.4.25.0" );

  std::vector<unsigned char> Vec =
       std::for_each( Src.begin(), Src.end(), Split( Src ) );

  std::copy( Vec.begin(), Vec.end(),
             std::ostream_iterator<int>( std::cout, "\n" ) );

  return 0;
 }

I understand the basic premise behind the call to for_each, which involves calling Split for each Src element from begin to end (or one past end).  Debuging the code shows a different story.

First the constuctor for Split gets called.  getline removes the token '.' and the object now contains 13 elements.

operator() (char)  gets called twenty seven (27) times.  I know because I placed  a static counter within operator().  This was a mystery to me, but then I discovered that each element within Src is treated as such.  A char.  So when operator() (char) gets called twice for '11' etc.

The confusion  stems from the fact that the code within the constructor  - as I understand it - has partitioned the Src object and placed the result in Vec.  What's the net gain for calling Split 27 times.  In essence operator() (char) isn't doing much or am I mistaken?

Consider:
  operator () (char)
  operator std::vector<unsigned char>() const { return Vec; }

The primary (used sparingly) difference between the two function objects is the latter specifies the return type while the former does not?  The latter is needed for visibility into Vec contained in Split?

Thanks in advance
0
Comment
Question by:forums_mp
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
  • 2
  • 2
6 Comments
 
LVL 19

Assisted Solution

by:drichards
drichards earned 20 total points
ID: 11852795
>> I understand the basic premise behind the call to for_each, which involves calling Split for each Src element from begin to end >> (or one past end).  Debuging the code shows a different story
No, you have misunderstood how the for_each works.  It calls the function for each element of Src from Src.begin() to Src.end().  For a string, the iterator goes one character at a time, so for each character in Src (there are 27 characters) 'operator()(char)' is called.

The end result is that the for_each calls the NO-OP operator a bunch of times.  You would achieve the same result with:

   std::string Src( "1.3.6.1.5.1.11.3.2.1.4.25.0" );
   std::vector<unsigned char> Vec = Split(Src);
   std::copy( Vec.begin(), Vec.end(),std::ostream_iterator<int>( std::cout, "\n" ) );

The really important part of your for_each is to construct the Split struct.  The iteration of for_each chews up some CPU cycles but doesn't accomplish anything.

And you do not have two function objects.  One function object with a cast operator and the 'function' operator.
0
 

Author Comment

by:forums_mp
ID: 11853922

So all the 'works' was been done the first time via the call to Split constructor.  Right?

I assume by NO-OP operator you mean operator () (char)?  I realize operator () (char) is doing nothing which I suspect constitues NO-OP, well......

I might add that I've not see anything about function operator in Jousittis but what makes this
  operator char*() { return "test" ; }
a function operator?

IOW, I interpret the above to mean.  An 'unnamed' function returning a char*.  The addition of the operator keyword adds a new 'dimension' but I'm unsure what.
0
 
LVL 19

Expert Comment

by:drichards
ID: 11855145
>> So all the 'works' was been done the first time via the call to Split constructor.  Right?
Exactly.  Merely constructing the object performs the split into the vector.

A function object (in this context) is nothing more that an object that implements an operator() with appropriate parameters.  In for_each, the parameter list needs to be a single parameter of whatever type the iterator dereferences to.  In your case it is char, so you provide 'operator() (char)'.

>>  The addition of the operator keyword adds a new 'dimension' but I'm unsure what.
An operator is not unnamed as you could call an operator as 'a.operator+(b)' rather than saying 'a+b' if you really wanted.  The difference is that operators may be called implicitly.
0
[Live Webinar] The Cloud Skills Gap

As Cloud technologies come of age, business leaders grapple with the impact it has on their team's skills and the gap associated with the use of a cloud platform.

Join experts from 451 Research and Concerto Cloud Services on July 27th where we will examine fact and fiction.

 
LVL 15

Expert Comment

by:efn
ID: 11857357
>> what makes this
>>  operator char*() { return "test" ; }
>>a function operator?

It's not a function operator, it's a conversion operator.  A function call operator is operator() (possibly with some parameters), so you could declare a function call operator that returns char* like this:

char* operator();

--efn
0
 

Author Comment

by:forums_mp
ID: 11877072

Here's I suspect one last question/example on this.  For some reason I still struggle with the function object syntax.  Consider

# include <iostream>
# include <ostream>

template<class NumT> struct limit_range
 {
  NumT Value;
  NumT MinValue;
  NumT MaxValue;

  explicit limit_range( NumT value, NumT Tmin, NumT Tmax )
           : Value( value ), MinValue( Tmin ), MaxValue( Tmax ) {}

    operator int() const
     {
      return  ( Value >= MinValue ) && ( Value <= MaxValue ) ?
                Value :
                throw std::runtime_error( "Value is out of range.\n" );
     }
 };

class SomeObject
 {
  private:
     int N;
  public:
     SomeObject( int n ) : N( limit_range<int>( n, 100, 200 ) ) {}

     void SetValue( int n ) { N = limit_range<int>( n, 100, 200 ); }
 };

int main()
 {
  try
   {
    SomeObject A( 150 );
    SomeObject B( 200 );

    B.SetValue( 201 );
   }
  catch( const std::runtime_error& e )
   {
    std::cerr << "ERROR: " << e.what() << std::endl;
   }

  return 0;
 }

How does the compiler transform the constructor call  "limit_range<int>( n, 100, 200 )" into operator int().
I'm thinking.  The constructor gets called and now I'm left with N().  However, N is not of type limit_range so how does the operator int() gets called?
0
 
LVL 15

Accepted Solution

by:
efn earned 30 total points
ID: 11877401
limit_range<int>( n, 100, 200 )

is not really a constructor call, it tells the compiler to construct a temporary limit_range object.  In general, if a class C has a default constructor, you can code C() to specify a temporary object of that class, and if it has a constructor with parameters, you can pass parameters in the parentheses.

So

 N( limit_range<int>( n, 100, 200 ) )

initializes the int N with the temporary limit_range object.  The compiler knows N is an int, so it expects an int to initialize it.  The limit_range object is not an int, but it has a conversion operator that converts to int, so the compiler automatically converts the limit_range to an int and uses the int to initialize N.  The general principle is that if you use an object of class C in a context that calls for an object of another type D, and C has a conversion operator that converts a C to a D, the compiler will automatically use the conversion operator.

There is no function object anywhere in this example.  For a type T,

T operator()

declares a function call operator that returns a T, while

operator T()

declares a conversion operator that converts to T.

--efn
0

Featured Post

On Demand Webinar: Networking for the Cloud Era

Ready to improve network connectivity? Watch this webinar to learn how SD-WANs and a one-click instant connect tool can boost provisions, deployment, and management of your cloud connection.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Here is a helpful source code for C++ Builder programmers that allows you to manage and manipulate HTML content from C++ code, while also handling HTML events like onclick, onmouseover, ... Some objects defined and used in this source include: …
How to install Selenium IDE and loops for quick automated testing. Get Selenium IDE from http://seleniumhq.org Go to that link and select download selenium in the right hand column That will then direct you to their download page. From that p…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

623 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question