Reputation: 1903
I have many template classes, which work in a arbitrary order together (share the same concept).
Supposing that I have:
template<typename T>
class A {
T t_;
public:
void call() {
// Do something.
t_.call();
}
};
template<typename T, typename U>
class B {
T t_;
U u_;
public:
void call() {
// Do something.
t_.call();
// Do something.
u_.call();
}
};
class C {
public:
void call() {
// Do something.
}
};
And I have the following instantiated class:
using Foo = A<B<A<C>,C>>;
Lets assume, C need a special constructor (or initializer function) to work probably. Something I do only know at runtime.
struct C {
void init(int);
void call();
};
How can I initialize Foo probably? Or any other nested class combination?
My current workaround is to define C as:
template<typename I>
struct C {
C() : var_(I::get())
void call();
};
And create Foo inside a function:
int main()
{
int i = 0;
struct HelperC1 {
static int get(bool set = false, int value = 0) {
static int value_ = value;
if (set) value_ = value;
return value_;
}
} helperC1;
struct HelperC2 {
static int get(bool set = false, int value = 0) {
static int value_ = value;
if (set) value_ = value;
return value_;
}
} helperC2;
helperC1.get(true, i);
helperC2.get(true, i+1);
A<B<A<C<HelperC1>>,C<HelperC2>>> foo;
foo.call();
return 0;
}
You see, this workaround is not very handy. Another approach would be to call the first constructor of Foo with the arguments and redirect them to C but this is very bad for different class combinations like:
using Bar = A<B<A<C>,<B<B<A,C>,C>>>;
Question: How to initialize nested (template) classes with a runtime arguments (better/in a nicer, cleaner way)?
Upvotes: 2
Views: 514
Reputation: 3107
This example builds values for branching template types with move construction and forwarding helper functions.
The forwarding helpers wrap constructors, using type inference to avoid the need to specify each constructor with its complicated parameters.
In this example, the call() function dumps the parameter structure and values of an object's children.
#include <iostream>
#include <utility>
template<typename T>
class A {
T t;
public:
A(T&& t) : t{std::move(t)} {}
void call() {
std::cout << "A<";
t.call();
std::cout << ">";
}
};
template <typename T>
inline A<T> mkA(T&& t) { return A<T>{std::forward<T>(t)}; }
template<typename T, typename U>
class B {
T t;
U u;
public:
B(T&& t, U&& u) : t{std::move(t)}, u{std::move(u)} {}
void call() {
std::cout << "B<";
t.call();
std::cout << ",";
u.call();
std::cout << ">";
}
};
template <typename T, typename U>
inline B<T,U> mkB(T&& t, U&& u) {
return B<T,U>{std::forward<T>(t), std::forward<U>(u)};
}
class C {
int c;
public:
C(int c) : c{c} {}
void call() {
std::cout << "C(" << c << ")";
}
};
int main() {
auto bar = mkA(mkB(mkA(C{1}), mkB(mkB(mkA(C{2}),C{3}), C{4})));
bar.call();
std::cout << '\n';
}
This outputs:
A<B<A<C(1)>,B<B<A<C(2)>,C(3)>,C(4)>>>
Upvotes: 1
Reputation: 4855
You could use pointers and build foo
with already constructed and initialized objects. ie:
UNTESTED CODE
template<typename T>
class A {
T* t_;
public:
A(T* valptr) : t_(valptr){}
~A(){ delete t_ ; }
void call() {
// Do something.
t_.call();
}
};
template<typename T, typename U>
class B {
T* t_;
U* u_;
public:
B(T* val1ptr, U* val2ptr):t_(val1ptr), u_(val2ptr){}
~B(){delete val1ptr; delete val2ptr;}
void call() {
// Do something.
t_->call();
// Do something.
u_->call();
}
};
class C {
private:
int x_;
public:
C(int x):x_(x){}
void call() {
// Do something.
}
};
Usage:
A<B<A<C>,C>> foo( new B<A<C>,C>(new A<C>(new C(3) ), new C(3) ) );
Upvotes: 2