views:

150

answers:

3

Now I am try to use boost bind & mem_fn. But there's a problem to bind overloaded-function. How to resolve compile error of follow codes?

boost::function< void( IF_MAP::iterator ) > bmf = std::mem_fun1< void, IF_MAP, IF_MAP::iterator >( &IF_MAP::erase );
boost::function< void( IF_MAP::iterator ) > bmf = boost::mem_fn< void, IF_MAP, IF_MAP::iterator >( &IF_MAP::erase );

The main purpose is to compile follow codes

IF_MAP M;
boost::function< void( IF_MAP::iterator ) > bmf = boost::bind(
    boost::mem_fn< void, IF_MAP, IF_MAP::iterator >( &IF_MAP::erase ),
    &M, _1 );
M.insert( IF_MAP::value_type( 1, 1.f ) ); M.insert( IF_MAP::value_type( 2, 2.f ) );
bmf( 2 );

The compile error messages are like this...

error C2665: 'boost::mem_fn' : none of the 2 overloads could convert all the argument types could be 'boost::_mfi::mf1 boost::mem_fnTraits>::iterator>(R (_thiscall std::map<_Kty,_Ty>::* )(A1))' or 'boost::_mfi::cmf1 boost::mem_fnTraits>::iterator>(R (_thiscall std::map<_Kty,_Ty>::* )(A1) const)'

P.S. As U know, std::map has 3 overloaded erase member function

  1. void erase(iterator _Where)
  2. size_type erase(const key_type& _Keyval)
  3. void erase(iterator _First, iterator _Last) 2nd function can be binded easily, but others not.

Edit
To describe my question in more detail:

Actually, I want to make deferred function call. When I meet return code of function, then it's time to scope out, so deferred function should be called.

Now I am refactoring some legacy codes. Nowdays, I usually see like this pattern of codes. (Actual codes are more complex but almost same as follows) Duplicated erase() calls are scattered in this function.

typedef map< int, float > IF_MAP;

bool DoAndPopOld( IF_MAP& M, int K )
{
    IF_MAP::iterator Itr = M.find( K );
    if ( Itr == M.end() ) return false;

    if ( K < 10 ) 
    {
        M.erase( Itr ); // erase call is here...
        return false;
    }

    if ( 100 < K )
    {
        // Do something
        M.erase( Itr ); // and here...
        return true;
    }

    // Do something
    M.erase( Itr ); // and also here!

    return true;
}

So, I wanna refactoring above code like this...

class ScopedOutCaller
{
private:
    boost::function< void() > F;
public:
    ScopedOutCaller( boost::function< void() > _F ) : F(_F) {}
    ~ScopedOutCaller() { F(); } // deferred function call
};

bool DoAndPopNew( IF_MAP& M, int K )
{
    IF_MAP::iterator Itr = M.find( K );
    if ( Itr == M.end() ) return false;

    // Make deferred call, so I do not consider calling erase function anymore.
    ScopedOutCaller SOC( boost::bind( &IF_MAP::erase ), &M, Itr );

    if ( K < 10 ) 
    {
        // M.erase( Itr ); <-- unnecessary
        return false;
    }
    if ( 100 < K )
    {
        // Do something
        // M.erase( Itr ); <-- unnecessary
        return true;
    }

    // Do something
    // M.erase( Itr ); <-- unnecessary
    return true;
}

But, as I asked... compile errors are occurred. The long and the short of what I want to do is how to defer function call. Please tell me the way to make deferred call. Thanks.

+3  A: 

std::maps member function erase() is overloaded, thus you have to manually disambiguate - see the Boost.Bind FAQ.

E.g. for the size_type erase(const key_type&) overload:

typedef IF_MAP::size_type (IF_MAP::*EraseType2)(const IF_MAP::key_type&);
boost::function<void (const IF_MAP::key_type&)> bmf2;
bmf2 = boost::bind((EraseType2)&IF_MAP::erase, &M, _1);

To select the other versions simply change the type you are casting to, e.g.:

// 1. void erase(iterator position) :
typedef void (IF_MAP::*EraseType1)(IF_MAP::iterator);
boost::function<void (IF_MAP::iterator)> bmf1;
bmf1 = boost::bind((EraseType1)&IF_MAP::erase, &M, _1);

// 3. void erase(iterator first, iterator last) :
typedef void (IF_MAP::*EraseType3)(IF_MAP::iterator, IF_MAP::iterator);
boost::function<void (IF_MAP::iterator, IF_MAP::iterator)> bmf3;
bmf3 = boost::bind((EraseType3)&IF_MAP::erase, &M, _1, _2);

Sadly Visual Studio is non-conforming to C++03 here (once again...) and you have to use the following two forms:

typedef IF_MAP::iterator (IF_MAP::*EraseType1)(IF_MAP::const_iterator);
typedef IF_MAP::iterator (IF_MAP::*EraseType3)(IF_MAP::const_iterator,
                                               IF_MAP::const_iterator);

With VC8 and VC9 you can solve that problem by using _HAS_STRICT_CONFORMANCE, but this breaks again with VC10 as C++0x changes the erase() overloads to the forms used by Dinkumware (see N3092, 23.4.1).
For portability i'd go for using a wrapper function instead to get around these annoying problems; if however you only care about VC just use the types i provided above.

To execute the resulting functors at block exit, the simplest way is to use Boosts shared_ptr or a similar scope guard. E.g. for the VC specific cast:

typedef IF_MAP::iterator (IF_MAP::*EraseType)(IF_MAP::const_iterator);
boost::shared_ptr<void> guard(static_cast<void*>(0),
                              boost::bind((EraseType)&IF_MAP::erase, &M, Itr));
Georg Fritzsche
Thans for your answer. Yeh.. as u mentioned, erase member function can be binded(I did it). But I wanna bind other overloaded function...like, void erase(iterator _Where) or void erase(iterator _First, iterator _Last). ^^; Do u have any idea?
codevania
@codevania: Sure, it is completely the same procedure - simply change the type you are casting to.
Georg Fritzsche
Thank you very much Georg. But above codes U suggested are not compiled because of 'type-cast error'.
codevania
My Compiler version is MSVS2005: Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
codevania
Georg Fritzsche
Exactly same error message is occurred on MSVS2010. So there is no way unless I user GCC compiler. T_T... Thanks any way. How kind of U to help.
codevania
codevania
Interesting: in C++0x, a lot of member functions that weren't previously overloaded are now overloaded with moving versions (e.g., `vector::push_back`). I suppose that if anyone is actually relying on those functions to not be overloaded is in for a surprise when they try to compile their code with a C++0x compiler. I guess that break in backwards compatibility was considered minor compared to the utility of the added overloads.
James McNellis
@James: As long as it results in compile errors instead of silent misbehaviour i think its fine. But changing the signatures of the existing overloads is a bit annoying portability-wise.
Georg Fritzsche
Using shared_ptr is very useful. thanks Georg!!
codevania
Thanks for your help. The problem was deducing template argument if I use boost::mem_fn. And another one was inappropriate function signature. It's o.k if I use erase() that returning iterator not void.
codevania
A: 
codevania
this should be part of the question.
jalf
@jalf: Yeah, I am same person as questioner. Something's wrong with log-in.
codevania
A: 

Try this:

ScopedOutCaller SOC(boost::bind(static_cast< IF_MAP::iterator (IF_MAP::*)(IF_MAP::const_iterator) >(&IF_MAP::erase), &M, Itr));

And for Visual Studio 2005:

ScopedOutCaller SOC(boost::bind(static_cast< IF_MAP::iterator (IF_MAP::*)(IF_MAP::iterator) >(&IF_MAP::erase), &M, Itr));
Yagur
I see... Correct signature is "IF_MAP::iterator (IF_MAP::*)(IF_MAP::iterator)" not "void (IF_MAP::*)(IF_MAP::iterator)"
codevania