PDA

View Full Version : variation of the named parameter idiom.


bladder
09-07-2005, 06:54 AM
Hey guys,

I just thought of experimenting with something today, a little variation on the named parameter idiom. It's a bit bloated, but I'm just messing around a little bit. Anyway, I came across a problem, my first guess is that this might not be allowed by the language in the first place - in which case I can use the boost preprocessor library to make a number of constructors, but I'd prefer that to be the last resort.

The troublesome bit of code is this pices:


struct NullParameter {
NullParameter() {}
void operator () ( void* o ) const
{}
};

class Texture
{
template< class P0 , class P1, class P2, class P3 >
Texture( const P0& p0 = NullParameter(),
const P1& p1 = NullParameter(),
const P2& p2 = NullParameter(),
const P3& p3 = NullParameter() )
: m_width(0),
m_height(0),
m_name(""),
m_useColorKey(false),
m_colorKey(0),
m_colorKeyAlpha(0)
{
p0(this);
p1(this);
p2(this);
p3(this);
// this->create();
}
};


It compiles fine if I privide arguments to all the parameters, but if it's not four, it complains about "does not take 3 arguments". So this would work:


Texture t( Texture::argSize( 500, 600 ),
Texture::argFilename( "happy" ),
Texture::argFormat( Texture::TF_Float32 ),
Texture::argColorKey( 0x0, 0x0 ) );


But this would not:


Texture t( Texture::argSize( 500, 600 ),
Texture::argFilename( "happy" ),
Texture::argFormat( Texture::TF_Float32 ) );


So is there a way to solve this problem without making multiple constructors? Incase you're wondering, Texture::argSize, Texture::argColorKey and the others are structures within the Texture class that overload operator () with a single parameter to a Texture*. The operator () overload is responsible for setting the private variables of the Texture object based on what was passed in at their time of construction: Eg: argColorKey would look like this:


class Texture
{
public:
//====
struct argColorKey {
//====
argColorKey( uint32 c, uint8 a ) : color(c), alpha(a)
{}
void operator () ( Texture* o ) const {
o->m_useColorKey = true;
o->m_colorKey = color;
o->m_colorKeyAlpha = alpha;
}
private:
uint32 color;
uint8 alpha;
};
};

.oisyn
09-07-2005, 07:33 AM
<s>Your code is fine, what compiler are you using?</s>

.edit: disregard that, looks like the constructor is not considered in the list of possible overloads for some reason... I'll have a look in the C++ spec

.edit2: Hmpff, how stupid of me to forget:
14.8.2.4-17- A template type-parameter cannot be deduced from the type of a function default argument. [Example:

template <class T> void f(T = 5, T = 7);
void g()
{
f(1); // OK: call f<int>(1,7)
f(); // error: cannot deduce T
f<int>(); // OK: call f<int>(5,7)
}

About the solution, you could overload the , operator to create a list of 'arguments' to your function (then you're going more in the direction of boost::parameter). The downside of this is that you'll need extra parentheses around your argument list. Or you can use another operator, such as <<, to circumvent that.

bramz
09-07-2005, 09:26 AM
This is just guessing ... I might be making a fool of myself now :)

I'm trying to "move" the default parameter to the template part ...


class Texture
{
template< class P0 = NullParameter, class P1 = NullParameter, class P2 = NullParameter, class P3 = NullParameter >
Texture( const P0& p0 = P0(),
const P1& p1 = P1(),
const P2& p2 = P2(),
const P3& p3 = P3() )


Bramz

bramz
09-07-2005, 09:35 AM
Myself, I use a construct like following for the named parameter idiom:



class Texture
{
class Arg
{
public:
Arg(): width_(0), height_(0), filename_("") {}
Arg& size(int width, int height) { width_ = width; height_ = height_; return *this; }
Arg& filename(const std::string& name) { filename_(name); return *this;
private:
friend class Texture;
int width_;
int height_;
std::string filename_;
};

Texture(const Arg& iArgs):
width_(iArgs.width_),
height_(iArgs.height_),
filename_(iArgs.filename_)
{
}
};

Texture t(Texture::Args().size(256, 256).filename("foo"));



Of course, your problem is how you can do it the other way :)

.oisyn
09-07-2005, 10:13 AM
This is just guessing ... I might be making a fool of myself now :)
Unfortunately, default template arguments are only allowed for class templates, not for function templates :wink:

bramz
09-07-2005, 10:18 AM
This is just guessing ... I might be making a fool of myself now :)
Unfortunately, default template arguments are only allowed for class templates, not for function templates :wink:
20968


bummer :)

bladder
09-07-2005, 09:38 PM
well that sucks...

About the solution, you could overload the , operator to create a list of 'arguments' to your function (then you're going more in the direction of boost::parameter). The downside of this is that you'll need extra parentheses around your argument list. Or you can use another operator, such as <<, to circumvent that.

Yeah those are solutions, another one would be to use the boost.preprocessor library to mechanically generate the different constructor overloads. but anyway, thatnks guys.