Solved

Function Objects

Posted on 2004-08-20
6
246 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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

[Webinar] Learn How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

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

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
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 viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

734 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