variation of the named parameter idiom.

22b3033832c5c699c856814b0cf80cb1
0
bladder 101 Sep 07, 2005 at 12:54

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;
 };
};

6 Replies

Please log in or register to post a reply.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Sep 07, 2005 at 13:33

<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.

25bbd22b0b17f557748f601922880554
0
bramz 101 Sep 07, 2005 at 15:26

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

25bbd22b0b17f557748f601922880554
0
bramz 101 Sep 07, 2005 at 15:35

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 :)

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Sep 07, 2005 at 16:13

@bramz

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:

25bbd22b0b17f557748f601922880554
0
bramz 101 Sep 07, 2005 at 16:18

@.oisyn

@bramz

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:

[snapback]20968[/snapback]

bummer :)

22b3033832c5c699c856814b0cf80cb1
0
bladder 101 Sep 08, 2005 at 03:38

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.