Subscribe to Karel-Lodewijk's Blog        RSS Feed
-----

[C++11] Named parameter idiom

Icon Leave Comment
I was getting my hands dirty a little with variadic templates and wanted to see if I can make a named parameter idiom. For those not familiar with it, It is a function where each of the parameters has a name and the order of the parameters is not important. I tried a little surface calculation function with a shape parameter and and a radius/width/height function.

First I tried to get it setup in such a way that the order of the parameters no longer matters.

So I want

surface("circle", 5.0)
surface(5.0, "circle")

Well I want this to look like a function call but I will probably have to go through an object at some point. So I decided to let the constructor word as a function call. This is what I came up with for parsing the parameters in random order.

class surface {
  public:
    template <class... Types>
    surface(Types... tail) {
        expand(tail...);
    }

    void expand() {}
    
    template <class Head, class... Tail>
    void expand(Head head, Tail... tail) {
        configure(head);
        expand(tail...);
    }

    std::string shape;
    double radius;
    
    void configure(std::string n) {
        shape = n;
    }

    void configure(double n) {
        radius = n;
    }
};



From top to bottom, I declared a constructor that takes any number of parameters, then the expand calls configure on the parameters one by one. And configure is an overloaded member function that sets the right member variable based on type.

Now to get a value back like you would from a function. The solution was the implicit conversion operator.

    operator double() {
        double result;
        if (shape == "circle") {
            result = M_PI*radius*radius; 
        } else if (shape == "square") {
            result = radius * radius;
        }
    }



This function makes certain a surface object can be implicitly converted to a double in any context that needs one. And sure enough.

    std::cout << surface("circle", 5.0) << std::endl; 
    std::cout << surface("square", 5.0) << std::endl; 
    std::cout << surface(5.0, "circle") << std::endl; 
    std::cout << surface(5.0, "square") << std::endl; 



Worked perfectly.

As a side effect, I've actually made surface a functor with lazy evaluation. Only when it is implicitly cast to a double does the functor calculate the surface, as long as it stays as a surface object it doesn't calculate anything.

Of course this is no good if you have 2 parameters with the same type, so that's what the named parameter is trying to solve. The goal is to make a function call that does this.

surface(shape("circle"), radius(5.0))
surface(shape = "circle", radius = 5.0)

Well the type overloaded configure functions worked pretty good, if we can make those shape/circle a type, then we're all done.

So I tried defining a subclass.

class surface {
    class shape {
      public:
        shape(std::string x) {
            value = x;
        }
        std::string value;
    };
};



Basically when I call surface like this:

surface(surface::shape("circle"))

The shape constructor gets called with "circle" as a parameter. Then the surface constructor gets the shape object and we do the same as we did before:

    void configure(shape x) {
        m_shape = x.value;
    }



This is where I stopped as I realized it can be done this way but as is often the case, boost has done it better. The surface:: namespace thing can be removed by moving the shape object outside the surface class.

The alternate syntax.

surface(shape = "circle", radius = 5.0)

can be accomplished by creating a static variable called shape, instead of a type called shape and overloading the = operator, and also the () operator for good measure, both operators should then return something that surface can easily parse.

You can also create a define macro to make the process easy. In the final program, I've implemented 4 options and the code is almost identical for all 4.

Spoiler

0 Comments On This Entry

 

Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

July 2014

S M T W T F S
  12345
6789101112
13141516171819
20212223 24 2526
2728293031  

Tags

    Recent Entries

    Search My Blog

    0 user(s) viewing

    0 Guests
    0 member(s)
    0 anonymous member(s)

    Categories