Solved

Function Objects

Posted on 2004-08-20
6
231 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
  • 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
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
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

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Jaspersoft Studio is a plugin for Eclipse that lets you create reports from a datasource.  In this article, we'll go over creating a report from a default template and setting up a datasource that connects to your database.
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.
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…

705 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

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now