views:

119

answers:

4

I am attempting to create a template "AutoClass" that create an arbitrary class with an arbitrary set of members, such as:

AutoClass<int,int,double,double> a;
a.set(1,1);
a.set(0,2);
a.set(3,99.7);
std::cout << "Hello world! " << a.get(0) << " " << a.get(1) << " " << a.get(3) << std::endl;

By now I have an AutoClass with a working "set" member:

class nothing {};

template <  typename T1 = nothing, typename T2 = nothing, typename T3 = nothing,
            typename T4 = nothing, typename T5 = nothing, typename T6 = nothing>
class AutoClass;

template <>
class AutoClass<nothing, nothing, nothing,
                nothing, nothing, nothing>
{
    public:
    template <typename U> void set(int n,U v){}
};

template <  typename T1, typename T2, typename T3,
            typename T4, typename T5, typename T6>
class AutoClass: AutoClass<T2,T3,T4,T5,T6>
{
    public:
    T1 V;
    template <typename U> void set(int n,U v)
    {
        if (n <= 0)
            V = v;
        else
            AutoClass<T2,T3,T4,T5,T6>::set(n-1,v);
    }
};

and I started to have problems implementing the corresponding "get". This approach doesn't compile:

template <  typename T1, typename T2, typename T3,
            typename T4, typename T5, typename T6>
class AutoClass: AutoClass<T2,T3,T4,T5,T6>
{
    public:
    T1 V;
    template <typename U> void set(int n,U v)
    {
        if (n <= 0)
            V = v;
        else
            AutoClass<T2,T3,T4,T5,T6>::set(n-1,v);
    }
    template <typename W> W get(int n)
    {
        if (n <= 0)
            return V;
        else
            return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
    }
    template <> T1 get(int n)
    {
        if (n <= 0)
            return V;
        else
            return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
    }
};

Besides, it seems I need to implement get for the <nothing, nothing, nothing, nothing, nothing, nothing> specialization. Any Idea on how to solve this?

+3  A: 
Seth Johnson
I know about boost::tuple. However I want to add more methods to this class template that are not on boost::tuple. So I tried to implement it this way. But I agree that usually it is better to use known libraries instead of reinvent the wheel.
ChAoS
Have you tried to simply add extra functionality to boost::tuple by making new functions? What extra functionality are you looking for?
Noah Roberts
Perhaps you could subclass it or add extra templated functions to implement that other functionality.
Seth Johnson
I was looking for a way to use the members of this AutoClass to automatically feed an arbittrary function (checking the number and types of arguments at compile time)
ChAoS
@ChAoS: If what you're doing isn't a learning exercise, definitely use boost. I think what you want is possible, so feel free to ask a question on how to do that. Just a quick glance makes me thing of using Boost.PP to generate N functions that unpack the tuple into the arguments of the desired function. You'd use it like `tuple_call(theTuple, someFunction)`.
GMan
A: 
Noah Roberts
Yes, I am aware of this problem. Maybe what you commented about putting the index n into the template like `a.get<2>()` is a good Idea, but I don't see how to do it.
ChAoS
you need to write at least one more metafunction: return_type<autoclass, n>. Then your function would end up having the signature: template < int n> typename return_type<this_autoclass, n>::type get(); Use similar methods that you have so far but use template specializations as your recursion rather than functional recursion.
Noah Roberts
+1  A: 

As others mentioned, you probably should be able to get where you want by reusing existing implementations from Boost or elsewhere.

If you would be doing something that can't be done using those or if you're curious:

  • try to keep the pseudo-variadic templates out of the implementation
  • use type-lists instead to allow for recursive meta-functions etc.
  • use pseudo-variadic templates as an interface if needed that forwards to the implementation
  • do as much at compile-time as possible, especially checks for indices etc.

A simple approach, utilizing MPL for convenience could look something like this:

template<class Types, size_t N> struct holder 
  // recursively derive from holder types:
  : holder<Types, N-1> 
{
    typename boost::mpl::at_c<Types,N>::type value;
};

// specialization that terminates the recursive derivation:
template<class Types> struct holder<Types,0> {
    typename boost::mpl::at_c<Types,0>::type value;
};

template<class Types>
class AutoClass 
  // recursively derive from holder types:
  : holder<Types, boost::mpl::size<Types>::value-1>    
{
    enum { n = boost::mpl::size<Types>::value };
public:
    template<size_t N, class U> void set(const U& u) {
        // index check at compile time:
        BOOST_STATIC_ASSERT((N < n));
        // cast to responsible holder base:
        static_cast<holder<Types,N>*>(this)->value = u;
    }
    template<size_t N> typename boost::mpl::at_c<Types,N>::type get() const { 
        // index check at compile time:
        BOOST_STATIC_ASSERT((N < n));
        // cast to responsible holder base:
        return static_cast<const holder<Types,N>*>(this)->value;
    } 
};

Usage:

typedef boost::mpl::vector<int,std::string> Types;
AutoClass<Types> a;

a.set<0>(42);
assert(a.get<0>() == 42);
a.set<1>("abcde");
assert(a.get<1>() == "abcde");

Keep in mind that this can still be wrapped with pseudo-variadic templates for end-user-convenience.

Georg Fritzsche
+3  A: 

First of all, I prefer Boost.Fusion to Boost.Tuple as it supports a better mixin of template metaprogramming and runtime algorithms I think.

For example, I'd like to present you a little marvel:

struct Name {}; extern const Name name;
struct GivenName {}; extern const GivenName givenName;
struct Age {}; extern const Age age;

class Person
{
public:
  template <class T>
  struct value
  {
    typedef typename boost::fusion::result_of::at_key<data_type const,T>::type type;
  };

  template <class T>
  struct has
  {
    typedef typename boost::fusion::result_of::has_key<data_type,T>::type type;
  };

  template <class T>
  typename value<T>::type
  get(T) { return boost::fusion::at_key<T>(mData); }

  template <class T>
  Person& set(T, typename value<T>::type v)
  {
    boost::fusion::at_key<T>(mData) = v; return *this;
  };

private:
  typedef boost::fusion::map <
    std::pair<Name, std::string>,
    std::pair<GivenName, std::string>,
    std::pair<Age, unsigned short>
  > data_type;
  data_type mData;
};

It's really fun to use:

Person p;
p.set(name, "Rabbit").set(givenName, "Roger").set(age, 22);

Well, I myself prefer indexing by classes than by indices, because I can convey meaning as well as adding type checking ;)

Matthieu M.
I like the idea of tag based indexing, +1.
Georg Fritzsche
There another benefit to using a Boost.Fusion collection to hold the data > it's almost as if you had gained reflexion, and you can therefore automatize printing (for debugging), serialization and deserialization, encoding and decoding of messages etc...
Matthieu M.