Search code examples
c++boostboost-bindc++03

Retrieve pointer to best match from overload set without calling


For various reasons I need to use 2 phase construction, furthermore the last phase is deferred and performed by another thread, some context:

...
#define BOOST_PP_LOCAL_MACRO(n) \
        template < typename ConnectionType, BOOST_PP_ENUM_PARAMS(n, typename T) > \
        boost::shared_ptr< ConnectionType > Connect( BOOST_PP_ENUM_BINARY_PARAMS(n, T, arg) ) \
        { \
            boost::shared_ptr< ConnectionType > con( boost::make_shared< ConnectionType >() ); \
            boost::mutex::scoped_lock sl( m_AddNetworkJobMutex ); \
            m_NetworkJobs.push_back( boost::bind( static_cast< void ( ConnectionType::* )( BOOST_PP_ENUM_PARAMS(n,T) ) >( &ConnectionType::Init ), con, BOOST_PP_ENUM_PARAMS(n, arg) ) ); \
            return con; \
        } 

#define BOOST_PP_LOCAL_LIMITS (1, 5)
#include BOOST_PP_LOCAL_ITERATE()
...

The problem here is that I want to select the best match possible from the overload set for ConnectionType::Init, but the cast is distinct and can't find a perfect match even if some of the arguments are convertible. So the questions becomes: Is it possible to somehow get the type & pointer to the best match from the overload set without actually calling it? Can't use anything which isn't available in C++03.


Solution

  • You can leverage lazy evaluation expression templates.

    AFAIK bind expressions are precisely in that family (as are Boost Proto epxressions, Spirit Grammar parse expressions etc.).

    Update finally got my act together. However, it only works with callable objects with overloaded operator(). I suppose you can use something like this as glue?

    • I now show both C++03 and C++11 proofs-of-concept that might help get something of a glue functor built-up around this
    • The C++03 is near equivalent (see the // TODO in the code)
    • The C++03 version depends on Boost Typeof and Boost Bind (see Boost Utility doc for result_of for backgrounds on result types of polymorphic function objects)
    • Both versions live on IdeOne

    C++03 (live on https://ideone.com/VHcEC)

    Here is a partial port of the C++11 demo (below) into C++03 + Boost:

    #include <string>
    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/typeof/typeof.hpp>
    
    struct overloaded
    {
        typedef int result_type;
    
        int operator()(const std::string& s) const { return 1; }
        int operator()(double d)             const { return 2; }
    };
    
    struct factory
    {
        template <typename T> struct result { typedef BOOST_TYPEOF_TPL(boost::bind(overloaded(), T())) type; };
    
        template <typename T>
        typename result<T>::type operator()(const T& t) const 
        {
            return boost::bind(overloaded(), t);
        }
    };
    
    int main()
    {
        overloaded foo;
    
        // based on local bind expression:
        BOOST_AUTO(unresolved, boost::bind(foo, _1));
        std::cout << unresolved("3.14") << std::endl;  // should print 1
        std::cout << unresolved(3.14)   << std::endl;  // should print 2
    
        // based on a factory function template
        factory makefoo;
        std::string str("3.14"); // TODO get rid of this explicit instanciation?
        std::cout << makefoo(str)() << std::endl;  // should print 1
        std::cout << makefoo(3.14)()   << std::endl;  // should print 2
    }
    

    C++11 (live on https://ideone.com/JILEA)

    As a simple example, this should work alright:

    #include <string>
    #include <iostream>
    #include <functional>
    
    using namespace std::placeholders;
    
    struct overloaded
    {
        int operator()(const std::string& s) const { return 1; }
        int operator()(double d)             const { return 2; }
    };
    
    template <typename T>
    auto makefoo(const T& t) -> decltype(std::bind(overloaded(), t))
    {
        return std::bind(overloaded(), t);
    }
    
    int main()
    {
        overloaded foo;
    
        // based on local bind expression:
        auto unresolved = std::bind(foo, _1);
        std::cout << unresolved(3.14)   << std::endl;  // should print 2
        std::cout << unresolved("3.14") << std::endl;  // should print 1
    
        // based on a factory function template
        std::cout << makefoo(3.14)()   << std::endl;  // should print 2
        std::cout << makefoo("3.14")() << std::endl;  // should print 1
    }