0

Is it possible to address the following scenario of choosing the argument at runtime with mpl?

struct A {
    A(int number) { /* use number */ } };
struct B { };

template <typename T, int n>
struct A_or_B_Holder {
    A_or_B_Holder() : _t( /* void or 'n', depends on T */ ) { }
private:
    T _t;
};

A_or_B_Holder<B, void> b;
A_or_B_Holder<A, 3> a;

Ideally,

A_or_B_Holder<B> b;
A_or_B_Holder<A, 3> a;
  • Sounds like a Boost variant... – Kerrek SB Aug 30 '11 at 22:02
  • Is creating specializations for A_or_B_Holder out of the question? – Mooing Duck Aug 30 '11 at 22:04
  • Specializations are out of the question - I'd have to create many specializations for each private member of types T, T_1, T_2, ... etc. What I'd like is to instruct the template that in one case I want the T member to be created with a 'void' argument and in another with '3'. – Bob Speaker Aug 30 '11 at 22:08
  • @Bob: So what you are really asking about is a `A_or_B_or_C_or..._Holder`? – UncleBens Aug 31 '11 at 06:44

3 Answers3

1

Your first problem is that void is a type, not an integer. You could make the template accept two types, the second being either boost::mpl::int_ or void.

Then you could either specialize the entire struct, or you could put the data member in a base class and specialize that.

#include <boost/mpl/int.hpp>

struct A {
    A(int number) { /* use number */ } };
struct B { };

template <class T, class Value>
struct A_or_B_Holder_Base{
    A_or_B_Holder_Base(): _t(Value::value) {}
protected:
    T _t;
};

template <class T>    
struct A_or_B_Holder_Base<T, void> {
    A_or_B_Holder_Base(): _t() {}
protected:
    T _t;
};

template <typename T, typename Value>
struct A_or_B_Holder : public A_or_B_Holder_Base<T, Value> {

};

using boost::mpl::int_;
A_or_B_Holder<A, int_<3> > x;
A_or_B_Holder<B, void> y;
A_or_B_Holder<A, void > w;  //error, no default constructor
A_or_B_Holder<B, int_<3> > z;  //error, no int constructor

More natural might be not to require the parameter to be a compile-time constant (as you are turning the compile-time constant into a run-time variable anyway). Just overload the constructor.

struct A {
    A(int number) { /* use number */ } };
struct B { };


template <typename T>
struct A_or_B_Holder {
    A_or_B_Holder() : _t( ) { }
    A_or_B_Holder(int number): _t(number) {}
private:
    T _t;
};

A_or_B_Holder<B> b;
A_or_B_Holder<A> a(3);
UncleBens
  • 40,819
  • 6
  • 57
  • 90
  • You should also add a `template struct Value {enum {value=val};};` and some sample usage code. – Mooing Duck Aug 30 '11 at 22:21
  • Also, a warning that T types must be copy-constructable. – Mooing Duck Aug 30 '11 at 22:22
  • @Mooing: From all I can see, A and B **are** copy-constructible. But as to the edit, the whole attempt with compile-time constant arguments where they are **not required** seems flawed. – UncleBens Aug 30 '11 at 22:30
  • The problem is that a(3) is not viable if A_or_B_Holder must be used via its default constructor. In my case, a third party library which expects a type, specified at compile time, constructs it with its default ctor, then manipulating it and returning it back to me. – Bob Speaker Aug 30 '11 at 22:31
  • 1
    @Bob: Honestly, it really sounds like A_Holder and B_Holder should be separate types. – Mooing Duck Aug 30 '11 at 22:39
  • @Bob - what does the library do with the object? if it needs to manipulate it (templated library functions?) wouldn't you want to avoid using a container as your type? – Node Aug 30 '11 at 22:52
  • @Mooing: Edited so as not to require copy constructor. – UncleBens Aug 31 '11 at 06:49
  • +1 for code that looks just like mine, but not using a magic constant. – Mooing Duck Aug 31 '11 at 16:50
0

I think that passing different arguments at run time is impossible. You'll have to have a standard constructor that all T types will have. You can make all T types construct from std::vector<void*> and use that to pass a variable number of variable type arguments, but that's super dangerous.

However, your sample seems to actually be deciding at compile-time, not run-time, which is solvable.

const int NO_INT_PARAM=INT_MIN;
template <typename T, int n>
struct A_or_B_Base {
    A_or_B_Holder() : _t(n) { }
private:
    T _t;
};
template <typename T>
struct A_or_B_Base<T, NO_INT_PARAM> {
    A_or_B_Holder() : _t() { }
private:
    T _t;
};
template <typename T, int n=NO_INT_PARAM>
struct A_or_B_Holder : A_or_B_Base<T, n>{
    A_or_B_Holder() : A_or_B_Base() { }
    /* other functions*/
};

A_or_B_Holder<A, 3> a; //uses A(3) constructor
A_or_B_Holder<B> b;  //uses B() constructor
A_or_B_Holder<A> c; //compiler error! A() doesn't exist!
A_or_B_Holder<B, 5> d;  //compiler error! B(5) doesn't exist!
A_or_B_Holder<B, NO_INT_PARAM> e;  //same as b, uses B() constructor
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
0

You could use a Boost.Variant

#include <boost/variant.hpp>

struct A
{
};

struct B
{
    B(int)
    {
    }
};

int main()
{
    boost::variant<A, B> val;

    if (/*some condition*/)
    {
        val = A();
    }
    else
    {
        val = B(5);
    }
}

If you are more interested in how this is implemented, rather than what you can use, I suggest you check out the implementations of Boost.Any and Boost.Variant.

Node
  • 3,443
  • 16
  • 18
  • Judging by Bob's comment to UncleBen's answer after you posted this, some of those member functions might be required. – Mooing Duck Aug 30 '11 at 22:40