1

I have a problem with ambiguity in the following code while using the apply function. It is based on the stackoverflow question here.

1) I have a helper class, because templated functions as pointers are not allowed:

template<typename TT, class...Ts>
struct selector_helper
{
    static TT doSelect(Ts&&... params)
    {
        return iSelectable<Ts...>::select<TT>(std::forward<Ts>(params)...);
    }
};

I tried to run the apply function, but run into problems. I tried following two codes but no one with success:

2.1) error: apply: none of the 2 overloads could convert all the argument types

boost::tuple<Ts...> tpl;
return apply<T, Ts...>(selector_helper<T, Ts...>::doSelect, tpl);

2.2) here I get two errors:

  • Error 2 error C2782: 'Ret apply(Ret (__cdecl *)(Args...),boost::tuples::tuple &&)' : template parameter 'Args' is ambiguous
  • Error 3 error C2782: 'Ret apply(Ret (__cdecl *)(Args...),const boost::tuples::tuple &)' : template parameter 'Args' is ambiguous

    boost::tuple<Ts...> tpl; 
    return apply(selector_helper<T, Ts...>::doSelect,   tpl);
    

edit: more explanation:

Both examples are runned within select method, which is in a class:

class database
{
    template<typename T, typename...Ts>
    T select_(const std::string& sql)
    {
        // apply examples
    }
};

And called here:

database db;
User result = db.select_<User, int, std::string, std::string>
    ("select id, email, name from credentials_tbl limit 1");

What did I miss?

here is a full example, I hope it contains everything: (when running with Microsoft Visual Studio 2013 I get the same errors)

// apply_test.cpp : Defines the entry point for the console application.
//

#include <iostream>
#include <boost/noncopyable.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/tuple/tuple.hpp>

using namespace boost;

/* apply methods from https://stackoverflow.com/questions/687490/how-do-i-expand-a-tuple-into-variadic-template-functions-arguments#answer-6454211 */

// ------------- UTILITY---------------
template<int...> struct index_tuple{};

template<int I, typename IndexTuple, typename... Types>
struct make_indexes_impl;

template<int I, int... Indexes, typename T, typename ... Types>
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...>
{
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type;
};

template<int I, int... Indexes>
struct make_indexes_impl<I, index_tuple<Indexes...> >
{
    typedef index_tuple<Indexes...> type;
};

template<typename ... Types>
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...>
{};




// ----------UNPACK TUPLE AND APPLY TO FUNCTION ---------




template<class Ret, class... Args, int... Indexes >
Ret apply_helper(Ret(*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup)
{
    return pf(forward<Args>(get<Indexes>(tup))...);
}


template<class Ret, class ... Args>
Ret apply(Ret(*pf)(Args...), const tuple<Args...>&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(),
        tuple<Args...>(tup));
}

template<class Ret, class ... Args>
Ret apply(Ret(*pf)(Args...), tuple<Args...>&&  tup)
{
    return apply_helper<Ret, Args...>(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup));
}

/* end of apply methods */



/* class iSelectable */
template <typename...> struct iSelectable;

template<class...Ts>
class iSelectable
{
public:

    template<typename T>
    static T select(Ts&&...params)
    {
        return T(std::forward<Ts>(params)...);
    }

};


/* selector_helper */
template<typename TT, class...Ts>
struct selector_helper
{
    static TT doSelect(Ts&&... params)
    {
        return iSelectable<Ts...>::select<TT>(std::forward<Ts>(params)...);
    }
};

class postgres_database_impl : private boost::noncopyable
    , public boost::enable_shared_from_this<postgres_database_impl>
{
public:

    template<typename T, typename...Ts>
    T select_(const std::string& sql)
    {

            boost::tuple<Ts...> tpl;            


            /* This is where my errors occur: */

            return apply(selector_helper<T, Ts...>::doSelect, tpl);

    }

};

typedef boost::shared_ptr<postgres_database_impl> DatabaseImpl;

class Database : private boost::noncopyable
{
public:


    template<typename T, typename...Ts>
    T select_(const std::string& sql)
    {
        return impl_->select_<T, Ts...>(sql);
    }



private:
    DatabaseImpl impl_;

};





class User : public iSelectable < int, std::string, std::string >
{
public:

    User(int id, const std::string& name, const std::string& surname)
    {
        /* only for testing purposes */
        std::cout << name << ", ";
        std::cout << name << ", ";
        std::cout << surname << std::endl;
    }
};


int _tmain(int argc, _TCHAR* argv[])
{

    Database db;
    User result = db.select_<User, int, std::string, std::string>
        ("select id, email, name from credentials_tbl limit 1");

    return 0;
}
Community
  • 1
  • 1
Felix
  • 2,531
  • 14
  • 25

1 Answers1

3

Regardless of context of the question, I'd usually expect the selector_helper type to look more like:

template <typename...> struct iSelectable;

template<typename TT>
struct selector_helper
{
    template<class... Ts>
    static TT doSelect(Ts&&... params) {
        return iSelectable<Ts...>::template select<TT>(std::forward<Ts>(params)...);
    }
};

Note the extra template qualification on the nested select template. If select is a type (how can we know) you might need typename too: Where and why do I have to put the "template" and "typename" keywords?

UPDATE

Looking at the more complete example, this is exactly the problem. The template arguments to do_select are explicitly specified:

return apply(selector_helper<T, Ts...>::doSelect, tpl);

This won't work, because it suppresses template argument deduction, and the signature is forced to

User (&)(int&&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&)

Of course, though the tuple is actually

boost::tuples::tuple<int, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, ...>&

You can see there will be conflicting types deduced for Args....

I would actually simplify this by being less specific:

template <class Ret, class Tuple, class... Args, int... Indexes>
Ret apply_helper(Ret (*pf)(Args...), index_tuple<Indexes...>, Tuple&& tup) {
    return pf(std::forward<Args>(get<Indexes>(std::forward<Tuple>(tup)))...);
}

template <class Ret, class Tuple, class... Args> 
Ret apply(Ret (*pf)(Args...), Tuple&& tup) {
    return apply_helper(pf, typename make_indexes<Args...>::type(), std::forward<Tuple>(tup));
}

This compiles already

Live On Coliru

// apply_test.cpp : Defines the entry point for the console application.
//
#include <iostream>
#include <boost/noncopyable.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/make_shared.hpp>

#define _TCHAR char
using namespace boost;

/* apply methods from
 * https://stackoverflow.com/questions/687490/how-do-i-expand-a-tuple-into-variadic-template-functions-arguments#answer-6454211
 */

// ------------- UTILITY---------------
template <int...> struct index_tuple {};

template <int I, typename IndexTuple, typename... Types> struct make_indexes_impl;

template <int I, int... Indexes, typename T, typename... Types>
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> {
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type;
};

template <int I, int... Indexes> struct make_indexes_impl<I, index_tuple<Indexes...> > {
    typedef index_tuple<Indexes...> type;
};

template <typename... Types> struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {};

// ----------UNPACK TUPLE AND APPLY TO FUNCTION ---------
    template <class Ret, class Tuple, class... Args, int... Indexes>
    Ret apply_helper(Ret (*pf)(Args...), index_tuple<Indexes...>, Tuple&& tup) {
        return pf(std::forward<Args>(get<Indexes>(std::forward<Tuple>(tup)))...);
    }

    template <class Ret, class Tuple, class... Args> 
    Ret apply(Ret (*pf)(Args...), Tuple&& tup) {
        return apply_helper(pf, typename make_indexes<Args...>::type(), std::forward<Tuple>(tup));
    }

/* end of apply methods */

/* class iSelectable */
template <typename...> struct iSelectable;

template <class... Ts> struct iSelectable {
    template <typename T> static T select(Ts &&... params) { return T(std::forward<Ts>(params)...); }
};

/* selector_helper */
template <typename TT, class... Ts> struct selector_helper {
    static TT doSelect(Ts &&... params) { return iSelectable<Ts...>::template select<TT>(std::forward<Ts>(params)...); }
};

class postgres_database_impl : private boost::noncopyable,
                               public boost::enable_shared_from_this<postgres_database_impl> 
{
  public:
    template <typename T, typename... Ts> T select_(const std::string &sql) {
        boost::tuple<Ts...> tpl { -123, "fame", "something" };

        /* This is where my errors occur: */
        return apply(selector_helper<T, Ts...>::doSelect, tpl);
        return {123, "name", "something"};
    }
};

typedef boost::shared_ptr<postgres_database_impl> DatabaseImpl;

class Database : private boost::noncopyable {
  public:
    template <typename T, typename... Ts> T select_(const std::string &sql) { return impl_->select_<T, Ts...>(sql); }

  private:
    DatabaseImpl impl_ = boost::make_shared<postgres_database_impl>();
};

class User : public iSelectable<int, std::string, std::string> {
  public:
    User(int id, std::string name, std::string surname) 
        : id(id), name(std::move(name)), surname(std::move(surname)) {
    }

  private:
    int id;
    std::string name;
    std::string surname;

    friend std::ostream& operator << (std::ostream& os, User const& user) {
        return os << user.id << ", " << user.name << ", " << user.surname << "\n";
    }
};

int main() {
    Database db;
    User result =
        db.select_<User, int, std::string, std::string>("select id, email, name from credentials_tbl limit 1");

    std::cout << "Result: " << result << "\n";
}

Output:

Result: -123, fame, something

I think I see a lot of opportunity for further simplification, but to be honest I would need to know more about the goal to embark on that journey.

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633