C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Varying number of constructor arguments

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Glen Low
Guest





PostPosted: Thu Aug 26, 2004 7:44 am    Post subject: Varying number of constructor arguments Reply with quote



I have a template in my library macstl 0.2 that looks like this:

template <typename T, size_t n> class vec
{
...
};

For each value of n, I want to define a constructor that has exactly n
arguments e.g.

vec <int, 4> v (4, 5, 6, 7); // is a valid call, since n = 4, # of
args = 4
vec <int, 4> v (6, 4); // is an invalid call, since n = 4, # of args =
2

The allowable values of n are fairly small and bounded (n = 2, 4, 8,
16) so it's acceptable to make a solution that has a finite number of
variations.

These are the solutions I came up with so far, any comments and/or
better ones?

1. Define the guts in vec_base, then partially specialize vec on
various n to have those constructors. vec inherits from vec_base.

PROS: only the legal constructors are visible.
CONS: tedious to set up -- also need some twisty Barton-Nackman or
CRTP if operators need to be declared on vec.

2. Declare all the constructors in vec, but only define the ones that
are legal.

PROS: only one class.
CONS: turns compiler errors into linker errors. Metaprogramming that
relies on only those constructors being declared would fail for
example.

3. Turn the constructor into a templated constructor, then use our
trusty flavor-of-the-month (year?) enable_if to selectively enable the
right ones e.g.

template <typename T1, typename T2, typename T3, typename T4>
vec (T1 t1, T2 t2, T3 t3, T4 t4,
enable_if <n == 4 && is_convertible
Quote:
::type* = NULL);

Note that I would love to apply enable_if a non-templated constructor,
but it fails since the constructor gets instantiated when the type is
declared?

PROS: like #1, only legals are visible
CONS: looks extremely klunky...

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Carl Barron
Guest





PostPosted: Fri Aug 27, 2004 11:03 am    Post subject: Re: Varying number of constructor arguments Reply with quote



In article <9215d7ac.0408251818.62eca043 (AT) posting (DOT) google.com>, Glen Low
<glenlow (AT) pixelglow (DOT) com> wrote:

Quote:
I have a template in my library macstl 0.2 that looks like this:

template <typename T, size_t n> class vec
{
...
};

For each value of n, I want to define a constructor that has exactly n
arguments e.g.

vec <int, 4> v (4, 5, 6, 7); // is a valid call, since n = 4, # of
args = 4
vec <int, 4> v (6, 4); // is an invalid call, since n = 4, # of args =
2

The allowable values of n are fairly small and bounded (n = 2, 4, 8,
16) so it's acceptable to make a solution that has a finite number of
variations.

These are the solutions I came up with so far, any comments and/or
better ones?

1. Define the guts in vec_base, then partially specialize vec on
various n to have those constructors. vec inherits from vec_base.

PROS: only the legal constructors are visible.
CONS: tedious to set up -- also need some twisty Barton-Nackman or
CRTP if operators need to be declared on vec.

2. Declare all the constructors in vec, but only define the ones that
are legal.

PROS: only one class.
CONS: turns compiler errors into linker errors. Metaprogramming that
relies on only those constructors being declared would fail for
example.

3. Turn the constructor into a templated constructor, then use our
trusty flavor-of-the-month (year?) enable_if to selectively enable the
right ones e.g.

template <typename T1, typename T2, typename T3, typename T4
vec (T1 t1, T2 t2, T3 t3, T4 t4,
enable_if ::type* = NULL);

Note that I would love to apply enable_if a non-templated constructor,
but it fails since the constructor gets instantiated when the type is
declared?

PROS: like #1, only legals are visible
CONS: looks extremely klunky...

Write templated class to hold the const refs of the data, and copy

the data from these referemce to the internals of vec. Example.

template <typename T,int N> class vec_args_t;

template <typename T> class vec_args_t<T, 1 >
{
const T &x0;
public:
vec_args_t(const T &a0)Mad0(a0){}
void copy(T *out) { *out = x0;}
};

template <typename T> class vec_args_t<T, 2 >
{
const T &x0;
const T &x1;
public:
vec_args_t(const T &a0,const T &a1)Mad0(a0),x1(a1){}
void copy(T *out) const {*out++ = x0;,*olut++ = x1;}
};

//...

template <typename T>
inline
vec_args_t<T, 1 > vec_args(const T &x0){return vec_args_t<T, 1 >(x0);}

//...

template <typename T,int N> class vec
{
T data[N];
public:
explicit vec(const vec_args<T,N> & in) {in.copy(data);}
// ...
}

the template specializations of vec_args_t and helpers can be machine
geinerated..

usage becomes:
vec<int,5> a(vec_args(1,2,3,4,5));

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Andreas Harnack
Guest





PostPosted: Sat Aug 28, 2004 3:07 am    Post subject: Re: Varying number of constructor arguments Reply with quote



I'm afraid there's no way to do this directly, simply because C++ doesn't
allow you to specify a variable length argument list; unless of course you
count the ellipse, but this gives you no control over the types, note even
the
number of arguments passed.

You can, however, construct an object by induction from a single element and
a 'lower order' object, i.e. something like:

vec<int, 4> v(x4, vec<int,3>(x3, vec<int,2>(x2, ... )));


That works fine but is not really convenient. Some people use macros as
a shortcut, but that's not perfect either. I prefer to go the other way
round
by providing an operator that constructs a 'higher order' object from a
given
one:

template < class T, int n > struct vec {
...
vec < T, n+1 >
operator()(T const& x) { return vec < T, n+1 > (x,*this); }
};

template < class T > struct vec<0> {
...
vec() {}
};


This way you could say (using the copy structor):

vec<int,4> v( vec<int,0>()(x1)(x2)(x3)(x4) );


The initial object doesn't carry any information, so this could be made a
global constant object:

vec<int,0> v0;
vec<int,4> v( v0(x1)(x2)(x3)(x4) );

Both versions use to work fine for me.
Even more tempting, however, is the comma operator:

template < class T, int n >
vec < T, n > operator,(vec < T, n-1 > const& v, T const& x) {
return vec<T,n>(x, v); }
template < class T >
vec < T, 2 > operator, (T const& x1, T const& x2) {
return vec<T,2>(x2, vec<T,1>(x1, vec<T,0>())); }


This way you could write

vec<int,4> v( (x1, x2, x3, x4) );


which probably comes closest to what you want. I tried this once and
the general idea seems to work; however, I had some problems to get
the compiler pick the right overloaded operator.
(Maybe I used overloading to heavily :-)


Andreas



"Glen Low" <glenlow (AT) pixelglow (DOT) com> wrote

Quote:
I have a template in my library macstl 0.2 that looks like this:

template <typename T, size_t n> class vec
{
...
};

For each value of n, I want to define a constructor that has exactly n
arguments e.g.

vec <int, 4> v (4, 5, 6, 7); // is a valid call, since n = 4, # of
args = 4
vec <int, 4> v (6, 4); // is an invalid call, since n = 4, # of args =
2

The allowable values of n are fairly small and bounded (n = 2, 4, 8,
16) so it's acceptable to make a solution that has a finite number of
variations.

These are the solutions I came up with so far, any comments and/or
better ones?

1. Define the guts in vec_base, then partially specialize vec on
various n to have those constructors. vec inherits from vec_base.

PROS: only the legal constructors are visible.
CONS: tedious to set up -- also need some twisty Barton-Nackman or
CRTP if operators need to be declared on vec.

2. Declare all the constructors in vec, but only define the ones that
are legal.

PROS: only one class.
CONS: turns compiler errors into linker errors. Metaprogramming that
relies on only those constructors being declared would fail for
example.

3. Turn the constructor into a templated constructor, then use our
trusty flavor-of-the-month (year?) enable_if to selectively enable the
right ones e.g.

template <typename T1, typename T2, typename T3, typename T4
vec (T1 t1, T2 t2, T3 t3, T4 t4,
enable_if ::type* = NULL);

Note that I would love to apply enable_if a non-templated constructor,
but it fails since the constructor gets instantiated when the type is
declared?

PROS: like #1, only legals are visible
CONS: looks extremely klunky...

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Glen Low
Guest





PostPosted: Sun Aug 29, 2004 11:18 am    Post subject: Re: Varying number of constructor arguments Reply with quote

Quote:
usage becomes:
vec<int,5> a(vec_args(1,2,3,4,5));

Thanks for your suggestion and for Andreas's suggestion. My audience
will be people most familar with C, so I don't want to throw them in
the deep end re: templates and constructor objects. Also, construction
needs to be fast, and gcc (3.3) only enregisters temporary objects
when they are structs with one field only, so any solution with such
temporary constructor objects will have this problem.

In the end, your suggestion got me thinking, and I've decided to use
the Named Constructor idiom and a sort of riff off Barton Nackman:

template <typename T, size_t n> class vec_base;

template <typename T, size_t n> class vec: public vec_base <T, n>
{
...
};

template <typename T> class vec_base <T, 4>
{
public:
static vec <T, 4> set (T t1, T t2, T t3, T t4) { ... }
};

template <typename T> class vec_base <T, 8>
{
public:
static vec <T, 8> set (T t1, T t2, T t3, T t4, T t5, T t6, T t7,
T tCool { ... }
};

and called:

vec <int, 4>::set (5, 6, 7, Cool;

The nice thing about this is since "set" is not a constructor (of
vec), there's no need to use CRTP and vec_base is kept nice and lean;
I've moved some typedefs that used to be in traits classes into it,
while vec itself defines most of the common functionality.

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Francis Glassborow
Guest





PostPosted: Sun Aug 29, 2004 10:48 pm    Post subject: Re: Varying number of constructor arguments Reply with quote

In article <SeLXc.8949$ZS5.432 (AT) news (DOT) cpqcorp.net>, Andreas Harnack
<andreas_harnack (AT) web (DOT) de> writes
Quote:
I'm afraid there's no way to do this directly, simply because C++ doesn't
allow you to specify a variable length argument list; unless of course you
count the ellipse, but this gives you no control over the types, note even
the
number of arguments passed.

You can, however, construct an object by induction from a single element and
a 'lower order' object, i.e. something like:

Another option is to package the data as a string and pass that as an
argument to an std::string parameter. Warp this as a std::stringstream
and then extract the data in the body of the ctor.

However it would help considerably, I think, if we could find a clean
syntactic mechanism for using an array of elements to initialise a
container in its ctor-init list.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Andreas Harnack
Guest





PostPosted: Tue Aug 31, 2004 10:07 am    Post subject: Re: Varying number of constructor arguments Reply with quote


Yes, I've been thinking along that line too. What about:


int a[] = { 0, 1, 2, 3 };

namespace array
{
template < class T > T* begin(T a[]) { return a; }
template < class T, int n > T* end(T (&a)[n]) { return a+n; }
};

vector<int> v(array::begin(a), array::end(a));


Alternatively (or in addition) we might try to wrap an aggregate into a
fully featured container
of a constant size (haven't tried that yet):

namespace array
{
template < class T > class vect
{
T* v; int n;
public:
vect(T* a, int s): v(a), n(s) {}
T* begin() { return v; }
T* end() { return v+n; }
int size() { return n; }
// ... etc.
};

template < class T, int n > vect<T> vector(T (&a)[n]) { return
vect<T>(a, n); }
};

vector<int> v(array::vector(a));

We need to have a function template to create an appropriate object since as
far
as I understand only a function object can deduce the array size n.

Andreas



"Francis Glassborow" <francis (AT) robinton (DOT) demon.co.uk> wrote

Quote:
In article <SeLXc.8949$ZS5.432 (AT) news (DOT) cpqcorp.net>, Andreas Harnack
[email]andreas_harnack (AT) web (DOT) de[/email]> writes
I'm afraid there's no way to do this directly, simply because C++ doesn't
allow you to specify a variable length argument list; unless of course
you
count the ellipse, but this gives you no control over the types, note
even
the
number of arguments passed.

You can, however, construct an object by induction from a single element
and
a 'lower order' object, i.e. something like:

Another option is to package the data as a string and pass that as an
argument to an std::string parameter. Warp this as a std::stringstream
and then extract the data in the body of the ctor.

However it would help considerably, I think, if we could find a clean
syntactic mechanism for using an array of elements to initialise a
container in its ctor-init list.




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Glen Low
Guest





PostPosted: Tue Aug 31, 2004 10:17 am    Post subject: Re: Varying number of constructor arguments Reply with quote

Quote:
However it would help considerably, I think, if we could find a clean
syntactic mechanism for using an array of elements to initialise a
container in its ctor-init list.

If you have a compiler that accepts C99 and C++ in the same program
(as does gcc), then you could do:

template <typename T, size_t n> class vec
{
public:
vec (T param [n]);
};

and called

vec <int, 4> v ((int []) {2, 3, 4, 5});

Without the C99-ism, it would be a clunkier (reminscent of valarray
initialization):

int temp [] = {2, 3, 4, 5};
vec <int, 4> v (temp);

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andreas Harnack
Guest





PostPosted: Wed Sep 01, 2004 10:03 am    Post subject: Re: Varying number of constructor arguments Reply with quote

Hi Glen,

I think if you do it that way you can't check for the number of elements in
the array since T[n] is transformed into T*; To get round that you'll have
to
deduce the template argument from the parameter and that - as far as I
know -
works only for function templates (see my previous posting).

However, are you (or is anybody else Smile aware of attempts to standardize
such a class to wrap an initialized aggregate into a container? 'Cause I use
to sketch down such classes/templates rather ad hoc and that's not really
convenient

Andreas


"Glen Low" <glenlow (AT) pixelglow (DOT) com> wrote

Quote:
However it would help considerably, I think, if we could find a clean
syntactic mechanism for using an array of elements to initialise a
container in its ctor-init list.

If you have a compiler that accepts C99 and C++ in the same program
(as does gcc), then you could do:

template <typename T, size_t n> class vec
{
public:
vec (T param [n]);
};

and called

vec <int, 4> v ((int []) {2, 3, 4, 5});

Without the C99-ism, it would be a clunkier (reminscent of valarray
initialization):

int temp [] = {2, 3, 4, 5};
vec <int, 4> v (temp);

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Glen Low
Guest





PostPosted: Wed Sep 01, 2004 9:36 pm    Post subject: Re: Varying number of constructor arguments Reply with quote

Quote:
I think if you do it that way you can't check for the number of elements in
the array since T[n] is transformed into T*; To get round that you'll have
to
deduce the template argument from the parameter and that - as far as I
know -
works only for function templates (see my previous posting).

Guided by your post, my solution was just off by a punctuation mark:

template <int n> struct vec
{
vec (int (¶m) [n]) // need the &, perhaps to inhibit decay to pointer
{
}
};

int main ()
{
int x [100];
int y [99];

vec <100> z1 (x); // OK
vec <100> z2 (y); // error!
vec <3> z1 ((int []) {1, 2, 3}); // hmm, now this is an error even with C99
}

The above works in VS 7.1, gcc 3.3 and online Comeau.

However the last line is now an error even with C99 support, since it's a temp.

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andreas Harnack
Guest





PostPosted: Sat Sep 04, 2004 1:26 am    Post subject: Re: Varying number of constructor arguments Reply with quote


Yes, that works for me too, even for initialized aggregates;
it seems the ampersand really does the trick. (I found it
the standard 14.8.2.4-13 by the way.)

template <int n> struct vec
{
vec (int (¶m) [n]) {}
};

int main ()
{
int x [] = { 0, 1, 2, 3 };

// vec <3> z1 (x); // error!
vec <4> z2 (x); // OK
// vec <5> z3 (x); // error!
}

I tried gcc version 2.95.4 and 3.0.4 on Debian 3.0
and gcc 3.2 on Suse 8.1.

But still, is there any work going on regarding a template
vector class with size as template argument? Or should
that be ask in its own thread?

Andreas




"Glen Low" <glenlow (AT) pixelglow (DOT) com> wrote

Quote:

Guided by your post, my solution was just off by a punctuation mark:

template <int n> struct vec
{
vec (int (¶m) [n]) // need the &, perhaps to inhibit decay to pointer
{
}
};

int main ()
{
int x [100];
int y [99];

vec <100> z1 (x); // OK
vec <100> z2 (y); // error!
vec <3> z1 ((int []) {1, 2, 3}); // hmm, now this is an error even with
C99
}

The above works in VS 7.1, gcc 3.3 and online Comeau.

However the last line is now an error even with C99 support, since it's a
temp.

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Glen Low
Guest





PostPosted: Tue Sep 07, 2004 10:13 pm    Post subject: Re: Varying number of constructor arguments Reply with quote

Quote:
But still, is there any work going on regarding a template
vector class with size as template argument? Or should
that be ask in its own thread?

Funny you should ask, I'm working on something very similar, the vec
<T, n> template class. It's more oriented toward a valarray-like
implementation and is really mean for SIMD acceleration in an
object-oriented fashion, so the emphasis is on builtin value types for
T.

http://www.pixelglow.com/macstl/

(check the reference section for altivec, the precursor of my vec
template.)

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.