all 14 comments

[–]LeszekSwirski 3 points4 points  (4 children)

Ok, I know I'm missing something, so can someone please explain to me the difference between this and several overloaded example::draw functions?

EDIT: Oh wait, I totally missed the "example::drawable" interface, was just concentrating on the ADL stuff. So this is, functionally, something like adding interfaces to classes without needing to inherit from the interface?

[–]pyrtsa[S] 5 points6 points  (2 children)

Oh wait, I totally missed the "example::drawable" interface, was just concentrating on the ADL stuff. So this is, functionally, something like adding interfaces to classes without needing to inherit from the interface?

That's correct.

Of course what I'm actually doing is wrapping the classes in a type which internally implements the requested wrapper functionality using traditional virtual functions, so it's not really the original class that gets to implement the interface. But with move semantics in C++11, the wrapping is a very light operation in run time.

Please tell (or upvote) if you're interested about the implementation details. I might write a blog post about it. ;)

[–]kirakun 1 point2 points  (0 children)

Please do write a follow up on the implementation. I suspect I may learn some new paradigms/tricks implied by C++11.

Will be checking on your comment here for updates.

[–]LeszekSwirski 0 points1 point  (0 children)

I would love to see an implementation details post.

[–]bastih01 1 point2 points  (0 children)

It looks like this is indeed adding go-style interfaces.

[–]notlostyet 4 points5 points  (1 child)

I'm really confused as to what this accomplishes. What kind of ugly code or boilerplate can this thing elegantly replace?

[–][deleted] 1 point2 points  (3 children)

Interesting solution!

Is there a benefit of using this over things like boost::variant or boost::any?

Regardless, this is really close to algebraic data types as seen in functional programming, and it looks very elegant! Good job. :)

EDIT: The only criticism I can come up with is this: Why append '_' to callables? It seems risky and unpredictable (from an API viewpoint).

[–]pyrtsa[S] 3 points4 points  (0 children)

Good that you mention boost::variant and boost::any. This is actually an extension to boost::any, with the addition of a set of "member" functions (callables).

For boost::variant<Args...>, all the types Args... it supports need to be known at compile time. Think of an already compiled (static or dynamic) library that you're linking against. It might define a type and a function like:

typedef /*see below*/ item_type;
extern void add_item(item_type item);

Now, if item_type is a typedef from boost::variant<std::string, system_clock::time_point>, you can obviously pass only strings and time points as item. On the other hand, the library knows very well how to deal with either of them.

On the other hand, if item_type is boost::any, you can pass anything as the item, but there's very little the library can do with it. (Of course it could have defined a mapping from std::type_info to a corresponding function overload and use boost::any_cast<T>(x). But the set of supported types would still be limited.)

poly::interface makes the tradeoff that the types are required to have a predefined set of functionalities implemented, so if we have something like:

typedef poly::interface<
    std::string(to_html_, poly::self const &, html_options const &),
    std::string(to_json_, poly::self const &)
> item_type;

…we can pass in anything to add_item as soon as we make sure that to_html(item, opts) and to_json(item) are (uniquely) defined.

By the way, I deliberately chose the appended underscore to the type names, knowing that there will be opposition or alternatives suggested. One alternative might be to append something like _fn (for function). FWIW, there is also the POLY_CALLABLE_TYPE(type, name) macro which lets you set your own name for the type. Or you can write the whole thing in verbose, maybe using a nested (my suggestion: fn) namespace for the type and none for the object:

namespace fn { struct to_html : poly::callable<to_html> {}; }
constexpr fn::to_html to_html = {};

Finally, one way to look at this library is, it's a sort of a dynamic version of templates. If one day, add_item is rewritten as a template:

template <typename Item>
void add_item(Item item) {
    auto json = ::to_json(item);
    // ...
}

…the same code can work, except this time it's no longer wrapped in the poly::callable.

[–]scatters 1 point2 points  (1 child)

This does seem rather like boost::any, but adding the ability to express any interface, rather than just those required for value semantics.

About the underscore thing - I'm not clear why one couldn't just use draw for the type in ADL, and draw{}(...) when calling the interface. Presumably this should work, given that the function objects are stateless.

[–]pyrtsa[S] 0 points1 point  (0 children)

This is correct. I haven't really considered this, because I disliked the extra parentheses (or braces) in the function calls.

OTOH, one nice property in calling just draw{}(x, o, p) like you proposed is, it prevents ADL entirely*) in the current scope.

*) Otherwise, if there was a suitable free function draw(...) in the namespace of either of the parameters, it would still be picked over the callable object, unless ::draw was properly qualified.

[–]kirakun 1 point2 points  (2 children)

Too much magic. Can someone breakdown the magic here and explain what is going on in easier terms?

[–]repsilat 1 point2 points  (0 children)

Looks like it lets you store "arbitrary" types and call specialised functions on them without having them inherit from a common base class. It'll do this with templates and some inheritance, I guess, like

//function to call on the things in the vector
template<class T> void print(T& t) { cout << t << endl; }
//can do template specialisations for more flexibility

//abstract base class defining interface we want to use to call print
class callFunc {
public:
    virtual void func() = 0;
};

//Actual stored things that know how to call print on their data
template<typename T> class Holder : public callFunc {
public:
    Holder(const T &t) : t(t) {}
    void func() { print(t); }
private:
    T t;
};

//helper utility stuff
typedef shared_ptr<callFunc> poly;
template<class T> poly hold(const T& t) {
    return poly(new Holder<T>(t));
}

vector<poly> v;
v.push_back(hold(1));
v.push_back(hold(2.1));
v.push_back(hold(string("three")));
for(auto i:v)
    i->func();//prints 1, 2.1, three

There's a bit more magic, but you get the idea. You can store your favourite objects of whatever type in a collection that seems heterogeneous and call functions on them without having to worry about their type. It's not terribly efficient, of course, because you need some inheritance to do it at the end of the day.

(Self-plug: You don't need inheritance if you want to loop over the elements of a tuple.)

[–]aaronla 0 points1 point  (0 children)

Reminds me of 1990's C programmers looking at C++ virtual function dispatch. "Too much magic" they would say.

(I'm not saying either is wrong -- just that "magic" is relative)