Optional template constructor parameters?

B

Bo Peng

Dear List,

I have a class that may or may not have several features. For example,
there might be "partition" to d_data of obj. The code is like (please
ignore grammar mistakes):

template<typename T>
struct obj
{
vector<T> d_data;
obj(T init_value);
};

template<typename T>
struct Partition
{
vector<int> d_index;
Partition(vector<int> ind);
};

I do not want to create templates for each combination of features so I
would like to use something like a policy-based design:

template<typename T,
template <class s> class feature1,
template <class s> class feature2>
struct obj: public feature1<T>, feature2<T>
{
vector<T> d_data;
obj(T init_value);
};

typedef template<int, Partition> intDataWithPartition;

In this way, I can get data member d_index (and methods to operate on
it) from Parition class. However, this will not work since I can not
provide constructor to Partition<T> from obj::eek:bj(). Is there any
workaround? Am I going a totally wrong way?

Many thanks in advance.
Bo
 
T

tom_usenet

Dear List,

I have a class that may or may not have several features. For example,
there might be "partition" to d_data of obj. The code is like (please
ignore grammar mistakes):

template<typename T>
struct obj
{
vector<T> d_data;
obj(T init_value);
};

template<typename T>
struct Partition
{
vector<int> d_index;
Partition(vector<int> ind);
};

I do not want to create templates for each combination of features so I
would like to use something like a policy-based design:

template<typename T,
template <class s> class feature1,
template <class s> class feature2>
struct obj: public feature1<T>, feature2<T>
{
vector<T> d_data;
obj(T init_value);
};

typedef template<int, Partition> intDataWithPartition;

In this way, I can get data member d_index (and methods to operate on
it) from Parition class. However, this will not work since I can not
provide constructor to Partition<T> from obj::eek:bj(). Is there any
workaround? Am I going a totally wrong way?

You need to decide on the interface to the policies. In this case, it
looks like they need at least the d_data vector passed to their
constructors. e.g.

obj(T init_value)
:feature1<T>(d_data), feature2<T>(d_data)
{
}

Note that a) you can only pass a reference to members of obj to a base
class, and b) you mustn't do anything with that reference until the
obj constructor body is reached (since it is only by then that the
d_data member has been constructed).

Alternatively, you could add an init member to each policy (which
takes a templated obj& parameter probably), and call that in the obj
constructor, passing whatever is necessary.

The main thing is to pin down a required interface or interfaces that
the feature1 and feature2 policies must implement.

Tom
 
B

Bo Peng

tom_usenet said:
You need to decide on the interface to the policies. In this case, it
looks like they need at least the d_data vector passed to their
constructors. e.g.

obj(T init_value)
:feature1<T>(d_data), feature2<T>(d_data)
{
}
Note that a) you can only pass a reference to members of obj to a base
class, and b) you mustn't do anything with that reference until the
obj constructor body is reached (since it is only by then that the
d_data member has been constructed).

This is a strange way of doing things since feature1<T>() will be
Alternatively, you could add an init member to each policy (which
takes a templated obj& parameter probably), and call that in the obj
constructor, passing whatever is necessary.

The init member functions can only do default construction, but I need
to construct Partition with parameters which, from what I understand,
can only be passed from the parameters of obj::eek:bj(). For example:

obj::eek:bj(T init_value, optional vector<int> d_index if Parition is
featured):feature1<T>(d_index), ....
{

}

Since the numbers of parameters of obj::eek:bj() will change, even partial
instantiation will not work. There are something called boost::enable_if
but I am not sure if it can do the magic.

Bo
 
V

Victor Bazarov

Bo said:
This is a strange way of doing things since feature1<T>() will be
created before obj. Data passed to feature1<T> should be considered
junk, right?

The value of d_data is junk (uninitialised), but if your 'feature1'
expects a reference, then d_data is OK because the storage has already
been created and the reference to it would be a valid one. You cannot
use it in the 'feature1' constructor, except to store it or to take
its address.

From what I saw, you didn't provide enough information to give any
particular solution. How does 'feature1' ('Partition' in your case)
initialise itself? Does it have to have the vector (already filled in),
or would the size of that data vector be enough? Would it try to alter
any other member data in 'obj'?

V
 
B

Bo Peng

Victor said:
From what I saw, you didn't provide enough information to give any
particular solution. How does 'feature1' ('Partition' in your case)
initialise itself? Does it have to have the vector (already filled in),
or would the size of that data vector be enough? Would it try to alter
any other member data in 'obj'?

These are undecided details of features. Of course features can be
initialized and given meaningful values later. For example

template<typename T>
struct Partition
{
vector<int> d_index;
Partition():d_index(0){}; // default constructor
setPartition(vector<int> i){ d_index = i; );
};

template<typename T,
template <class s> class feature1,
template <class s> class feature2>
struct obj: public feature1<T>, feature2<T>
{
vector<T> d_data;
obj(int length, T init_value):d_data(length),
feature1<T>(), feature2<T>()
{
fill(d_data.begin(), d_data.end(), init_value);
};
};

typedef template<int, Partition> intDataWithPartition;

intDataWithPartition data(100, 1) // initialize d_data
vector<int> par(2);
par[0]=10, par[1]=90;
data.setPartition(par);

What I wanted was a customized constructor that can accept different
parameters when using differnt policies, something like

vector<int> par(2);
par[1]=10, par[2]=90;
intDataWithParition data(100, 1, par)

Bo
 
T

tom_usenet

typedef template<int, Partition> intDataWithPartition;

intDataWithPartition data(100, 1) // initialize d_data
vector<int> par(2);
par[0]=10, par[1]=90;
data.setPartition(par);

From your example it is unclear why Partition is a base class of obj -
it appears to have nothing to do with it. Can't it just be a separate
object? Inheritence is very tight coupling.

You appear to be using policies is a rather strange way. Policies are
there to change the behaviour of a class, not to extend its
functionality, and to change the behaviour of the class, the class
must call back into them. For this to happen, they must implement a
fixed interface. See "Modern C++ Design" for details.
What I wanted was a customized constructor that can accept different
parameters when using differnt policies, something like

vector<int> par(2);
par[1]=10, par[2]=90;
intDataWithParition data(100, 1, par)

Hmm, this is probably more work than it's worth. One approach would be
to provide lots of overloads of the constructor, and use
metaprogramming to work out how to forward the arguments to the policy
constructors. But why not just call setPartition, or define some
common requirements for the policies so that you don't need custom
code to handle each policy.

Tom
 
B

Bo Peng

tom_usenet said:
From your example it is unclear why Partition is a base class of obj -
it appears to have nothing to do with it. Can't it just be a separate
object? Inheritence is very tight coupling.

Partition is a partition to d_data in class obj, I have to use one of
the following ways to put them together:

class obj: class Partition // Partition provide d_index
class PartitionedObj : class obj // sounds more natural
class obj{ // a bit more difficult to use
class Partition par;
...
}

To use a policy-based approach, because there will be other features,
only the first form makes sense.
You appear to be using policies is a rather strange way. Policies are
there to change the behaviour of a class, not to extend its
functionality, and to change the behaviour of the class, the class
must call back into them. For this to happen, they must implement a
fixed interface. See "Modern C++ Design" for details.

I agree with this. Policy based design might not work for me here.
Please allow me to re-phrase my problem:

1. I have a base class obj
2. There might be several optional extensions to obj
3. users might want one or more extensions.

Currently, I am using a big obj with all the extensions. Most of them
are latent in the sense that they are in class obj but unused.

class obj{
feature1 f1;
feature2 f2;
...
obj(parameter_for_f1 = NULL, parameter_for_f2 = empty_vector
):f1(parameter_for_f1), f2(parameter_for_f2){ }
}

I am not satisfied with this big class (maybe this is the best solution)
but the following is too tedious when the combination of features get big:

class objFeature1:public baseobj{
feature1 f1;
objFeature1( parameter_for_f1)
:f1(parameter_for_f1){}
}
class objFeature2:public baseobj{
feature1 f2;
objFeature2( parameter_for_f2)
:f2(parameter_for_f2){}
}
class objFeature1And2:public baseobj{
feature1 f1;
feature2 f2;
objFeature1( parameter_for_f1, parameter_for_f2)
:f1(parameter_for_f1), f2(parameter_for_f2){}
}
....

Policy-based design is my attempt to treat these classes in a uniform way.

Hmm, this is probably more work than it's worth. One approach would be
to provide lots of overloads of the constructor, and use
metaprogramming to work out how to forward the arguments to the policy
constructors. But why not just call setPartition, or define some
common requirements for the policies so that you don't need custom
code to handle each policy.

I guess this is the way to go, but it is hard to find common interface
to these policies.

Thanks.
Bo
 
T

tom_usenet

I agree with this. Policy based design might not work for me here.
Please allow me to re-phrase my problem:

1. I have a base class obj
2. There might be several optional extensions to obj
3. users might want one or more extensions.

Currently, I am using a big obj with all the extensions. Most of them
are latent in the sense that they are in class obj but unused.

class obj{
feature1 f1;
feature2 f2;
...
obj(parameter_for_f1 = NULL, parameter_for_f2 = empty_vector
):f1(parameter_for_f1), f2(parameter_for_f2){ }
}

I am not satisfied with this big class (maybe this is the best solution)
but the following is too tedious when the combination of features get big:

class objFeature1:public baseobj{
feature1 f1;
objFeature1( parameter_for_f1)
:f1(parameter_for_f1){}
}
class objFeature2:public baseobj{
feature1 f2;
objFeature2( parameter_for_f2)
:f2(parameter_for_f2){}
}
class objFeature1And2:public baseobj{
feature1 f1;
feature2 f2;
objFeature1( parameter_for_f1, parameter_for_f2)
:f1(parameter_for_f1), f2(parameter_for_f2){}
}
...

Policy-based design is my attempt to treat these classes in a uniform way.

Ok, how about making each feature offer the typedef
"constructor_parameter_type" and the static method
"get_default_constructor_parameter" (feel free to shorten the names).
e.g.

class feature1
{
protected:
typedef std::vector<int> const& constructor_parameter_type;
static std::vector<int> get_default_constructor_parameter()
{
return std::vector<int>();
}

feature1(std::vector<int> const& param);
};


class NullFeature
{
protected:
typedef int constructor_parameter_type;
static int get_default_constructor_parameter()
{
return 0;
}
NullFeature(int){}
};

(with as many features are required:)
template <class Feature1 = NullFeature, class Feature2 = NullFeature>
class obj: public Feature1, public Feature2
{
public:
obj(typename Feature1::constructor_parameter_type p1 =
Feature1::get_default_constructor_parameter(),
typename Feature2::constructor_parameter_type p2 =
Feature2::get_default_constructor_parameter())
:Feature1(p1), Feature2(p2)
{
}
};

Hopefully the empty base optimization will keep sizeof(obj<>) to a
minimum.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,045
Latest member
DRCM

Latest Threads

Top