
AMDG After 6+ months I've finally gotten back to this. I'd like to get some feedback on my ideas before I continue. The current version is in the vault. The basic interface is derived from Alexander Nasonov's dynamic_any struct increment : concept_<increment, void(_&)> { template<class T> static void execute(T& t) { ++t; } } any<increment> x(1); increment()(x); assert(type_erasure::any_cast<int>(x) == 1); Note that unlike Boost.Function references will not work. int i; any<increment> x(boost::ref(i)); // NO!! The reason is that if references were allowed it would be to easy to kill yourself. (I've been there. I allowed references at first and then implemented operator+ in terms of operator+=. When the left argument is a reference this means that I made a shallow copy of the reference, called operator += on that, thus violating the expectation that operator+ does not modify its arguments) A function can also return another polymorphic object whose dynamic type is different. template<class R, class P2> struct at : concept<at<R, P2>, R(const P2&, int)> { template<class T> static typename T::value_type execute(const T& t, int i) { return(t[i]); } }; any<at<_1, _> > any_container(std::vector<int>(1, 2)); any<> value(at<_1, _>()(any_container, 0)); assert(type_erasure::any_cast<int>(value) == 2); In this example _ is the placeholder that refers to the current object. The placeholder _1 is used to refer to the return type of at. Since there is a mechanism for handling multiple objects, we can capture multiple objects simultaneously using a tuple: template<class P1, class P2> struct add : concept_<add<P1, P2>, P1(const P1&, const P2&)> { template<class T1, class T2> static T1 execute(const T1& t1, const T2& t2) { return(t1 + t2); } }; int array[] = {...}; type_erasure::tuple<_1, _2, where_<add<_1, _2> > > tuple(&array[0], 0); any<> pointer_to_second_element(add<_1, _2>()(type_erasure::get<0>(tuple), type_erasure::get<1>(tuple)); assert(type_erasure::any_cast<int*>(pointer_to_second_element) == array + 1); The first two arguments to tuple<...> specify which placeholders correspond to those elements. The element 0 will be match _1 and element 1 will match _2. Note that add is not a true multimethod. It's arguments must ultimately come from the same place. i.e. you can apply it as many times as you want since its return type is known to always be the same as its first argument, but you cannot mix another type in e.g. long instead of int. Another thing to note is that it is always okay to discard functions. In the last example the result of calling add itself supports add since it is the same type. I don't care about that so I construct an any<> from it. I haven't shown it here, but any<...> takes up to 10 parameters or an MPL sequence. I can assign one any<> to another which has an arbitrary subset of its functions. One that has both increment and decrement can be assigned to one that supports only increment or to one that supports only decrement. Future Work: I intend to enable boost::ref iff there are no mutating operations for the type. There should be a class for references, so that you can throw away some operations without making a copy. I can implement multimethods if (a) The return type is fixed at compile time and (b) Every type specifies (through a traits template) a list of possible overloads involving it. Functions should be able to handle cases such as template<class P1, class P2> struct some_function : concept_<some_function<P1, P2>, void(const P1&, const P2&)> { //... }; any<some_function<_, std::string> > a1; tuple<_1, _2, where_<some_function<_1, _2> > > t(a1, std::string("The quick brown fox jumps over the lazy dog.")); where some of the arguments are any<>s and others are are of known type. In Christ, Steven Watanabe

Steven Watanabe <watanabesj <at> gmail.com> writes:
AMDG
After 6+ months I've finally gotten back to this. I'd like to get some feedback on my ideas before I continue. The current version is in the vault.
The basic interface is derived from Alexander Nasonov's dynamic_any
Steven, I'm reading your code but there are 53 files full of meta-programming and reading them may take some time. Can you please give an overview of the implementation or highlight differences between your code and mine? Thanks, -- Alexander

AMDG Alexander Nasonov <alnsn <at> yandex.ru> writes:
Steven Watanabe <watanabesj <at> gmail.com> writes:
AMDG
After 6+ months I've finally gotten back to this. I'd like to get some feedback on my ideas before I continue. The current version is in the vault.
The basic interface is derived from Alexander Nasonov's dynamic_any
Steven, I'm reading your code but there are 53 files full of meta-programming and reading them may take some time. Can you please give an overview of the implementation or highlight differences between your code and mine?
Biggest implementation difference: I am using an explicit table of function pointers and a separate buffer rather than a virtual functions. All the metaprogramming is either for creating the table or figuring out how one table maps onto another. At the lowest level, I have two components: a) a buffer class that handles allocation/deallocation or the small object optimization. (detail/anything.hpp) b) A generic vtable that supports static initialization and table conversions. (this is in detail/table/*) There are two distinct versions of function calls. The first has a fixed return type. The second returns another polymorphic object. The version that returns an any<>, takes a pointer to a buffer as a parameter and constructs the result in it. The table of the result is managed from outside the function call. This implies that the dynamic type of the result is entirely determined by the dynamic types of the arguments. The advantage of this is that conversions between any<>s work smoothly. (The code for mapping the interface onto a set of function pointers is in concept.hpp, detail/dispatch/*) detail/metafunction just contains some utilities that I needed and one important file that probably belongs elsewhere: type_deduction.hpp. This file has the code for working out what types each of the placeholders represent. e.g. If you have an any<function_call<_1()> > and construct it from a boost::function<int()> then _ will map to boost::function<int()> and _1 will map to _1. It handles the related case of mapping placeholders to placeholders for a conversion. In Christ, Steven Watanabe

Steven Watanabe <steven <at> providere-consulting.com> writes:
Biggest implementation difference: I am using an explicit table of function pointers and a separate buffer rather than a virtual functions.
Great. I planned this optimization but never started implementing it. Some further questions: Why do you use more than one placeholder? What's a difference between _1 and _? In dynamic_any, I use only anyT placeholder. I didn't get the idea of tuples. What are they for? -- Alexander

AMDG Alexander Nasonov <alnsn <at> yandex.ru> writes:
Steven Watanabe <steven <at> providere-consulting.com> writes:
Biggest implementation difference: I am using an explicit table of function pointers and a separate buffer rather than a virtual functions.
Great. I planned this optimization but never started implementing it.
Some further questions: Why do you use more than one placeholder? What's a difference between _1 and _? In dynamic_any, I use only anyT placeholder. I didn't get the idea of tuples. What are they for?
Everything that you do in dynamic_any can be handled using only _. The other placeholders are for dealing with multiple types. Basically, I was trying to come up with a way to mimic C++0x concepts as much as possible at runtime. The tuples provide a way to capture functions involving multiple types at the same time. tuple<_1, _2, where_<addable<_1, _2> > > is two element tuple whose elements can be added. If I created one tuple<_1, _2, addable<_1, _2> > t(1, 2); Then, I can write type_erasure::any<> result = get<0>(t) + get<1>(t); assert(type_erasure::any_cast<int>(result) == 3); One of the things I was aiming for was typedef tuple<_1, _1, _2, where< forward_iterator<_1>, callable<_2, bool(deref<_1>::type)> > > algorithm_args; class Base { public: // "virtual" template template<class Iter, class F> void do_stuff(Iter begin, Iter end, F f) { algorithm_args args(begin, end, f); return(do_stuff_impl(args)); } virtual void do_stuff_impl(algorithm_args&) = 0; }; class Derived : public Base { virtual void do_stuff_impl(algorithm_args& args) { std::remove_if(get<0>(args), get<1>(args), get<2>(args)); } }; In Christ, Steven Watanabe

Steven Watanabe wrote:
After 6+ months I've finally gotten back to this. I'd like to get some feedback on my ideas before I continue. The current version is in the vault.
The basic interface is derived from Alexander Nasonov's dynamic_any
struct increment : concept_<increment, void(_&)> { template<class T> static void execute(T& t) { ++t; } }
any<increment> x(1); increment()(x); assert(type_erasure::any_cast<int>(x) == 1);
Doesn't adobe::poly provide similar things but with way nicer interfaces?

AMDG Mathias Gaunard <mathias.gaunard <at> ens-lyon.org> writes:
Steven Watanabe wrote:
After 6+ months I've finally gotten back to this. I'd like to get some feedback on my ideas before I continue. The current version is in the vault.
The basic interface is derived from Alexander Nasonov's dynamic_any
struct increment : concept_<increment, void(_&)> { template<class T> static void execute(T& t) { ++t; } }
any<increment> x(1); increment()(x); assert(type_erasure::any_cast<int>(x) == 1);
Doesn't adobe::poly provide similar things but with way nicer interfaces?
a) I'm not entirely convinced that adobe::poly's interfaces are nicer. Would you mind elaborating. If you mean that adobe allows ++x instead of increment()(x), I provide a template that can be specialized to enable such usage, too. poly's interface is optimized for interfaces that have many functions grouped together. Mine is optimized for having many independent functions. In terms of the amount of boilerplate, it's about a toss-up. b) I support multiple independent functions in such a way that any<increment, decrement> can be converted to any<increment> or any<decrement> or any<> without adding a layer of indirection. c) adobe::poly relies on undefined behavior to implement the small object optimization. (It assumes that a single base class is allocated at the beginning of the object). As far as I know, it is impossible to implement the small object optimization in a standard conformant way without either changing the interface away from virtual functions or adding an extra pointer to the poly<> object or having double indirection for the virtual table. In Christ, Steven Watanabe
participants (4)
-
Alexander Nasonov
-
Mathias Gaunard
-
Steven Watanabe
-
Steven Watanabe