Search code examples
c++templatesboosttype-traitsboost-phoenix

BOOST_PHOENIX_ADAPT_FUNCTION(...) with templated function on templated container


I need a lazy evaluating version of std::pair.first. My approach is to use boost::phoenix, define a templated function and use the BOOST_PHOENIX_ADAPT_FUNCTION makro as folows:

template <typename T1, typename T2>
T1 first_impl(std::pair<T1,T2> p){
    return p.first;
}
BOOST_PHOENIX_ADAPT_FUNCTION(std::string, first, first_impl, 1);

which is fine for the specific case I need in my current program (in my case T1 = std::string). But how can I use the result type T1 of the first_impl function template to abstract this even more for future use? The documentation mentions the use of typename remove_reference<A0>::type as first argument to the makro to define the return type of the function to be the type of its first argument. Based on this I tried several versions like:

BOOST_PHOENIX_ADAPT_FUNCTION(typename A0::first_type, first, first_impl, 1); 

trying to access std::pair<T1, T2>::first_type which doesn't seem to work for me.

Additionaly I tried to adapt what std::remove_reference does to deal with this like follows

template <typename T1, typename T2> first_type < std::pair<T1,T2> > 
{ typedef T1 type; }

but this doesn't seem to work either. Can someone point me to what I'm doing wrong here?


Solution

  • I'd suggest a trait, indeed like you almost had with first_type:

    namespace detail // by convention, hide details from enclosing namespace
    {
        template <typename Pair, typename First = typename std::remove_reference<Pair>::type::first_type> 
        struct first_type {
            typedef First type;
        };
    
    // Now you can use the trait in your `first_impl` return type:
    
        template <typename Pair>
        typename first_type<Pair>::type first_impl(Pair const& p){
            return p.first;
        }
    }
    

    Now, you can indeed use in the adaption:

    BOOST_PHOENIX_ADAPT_FUNCTION(typename detail::first_type<A0>::type, first, detail::first_impl, 1)
    

    Fully working demo: See it Live on Coliru

    int main()
    {
        using boost::phoenix::arg_names::arg1;
    
        std::map<std::string, int> const m { 
            { "one",   1 },
            { "two",   2 },
            { "three", 3 },
            { "four",  4 }
        };
    
        std::for_each(begin(m), end(m), std::cout << first(arg1) << "\n");
    }
    

    Output

    four
    one
    three
    two