
Hi Following the recent interest in clone pointers and Polymorphic value object wrappers, I developed a slightly feature enriched Polymorphic Value object, that implements the small object optimization, and models the Optional Pointee concept. It follows a non-intrusive approach to cloning. The code can be found in the boost vault: *http://tinyurl.com/3a6sq8* Motivation: Dynamic polymorphism is a valuable but underestimated tool to the modern C++ programmer. It for example allows the implementation of type erasure and other techniques for physical decoupling. It is also unavoidable every time dynamic dispatching is needed. Unfortunately solutions based on it may often be slow, because traditional dynamic polymorphism is always paired with dynamic allocation. The proposed tool is targeted to ease the usage of dynamic polymorphism in modern C++ context, by giving value semantics to polymorphic objects, as well as providing a well known optimization technique to avoid dynamic allocation when the involved objects are small. Dynamic polymorphism, value semantics and small object optimization constitute a powerful receipt to solve such problems. As evidence boost::function, among the most valuable tools for type erasure and physical decoupling available in boost, is using all three of them. This tool is meant to be a low level tool, on which powerful abstractions like boost::function could be built easily. Basic Usage Example: #include "optional_poly.hpp" #include <iostream> // class with one virtual method but not virtual destructor // optional_poly will know the derived type, so it will call the correct destructor nevertheless struct B { virtual operator int() {return 0;}}; // lack of virtual destructor is intentional // Derived class, with side effect in destructor to show functionality, and overloaded virtual method to determine which one was called struct D : B { operator int() {return 1;} ~D() { std::cerr<<"Got it\n"; } void swap(D&) {} private: void operator=(D const &); }; // swap reachable via ADL. optional_poly will prefer (by default) this to implement its swap method. Using a trait, you can specify if a swap method or no swap at all should be used for a given type void swap(D& a,D& b) { a.swap(b); } int main() { // optional_poly constructor and operator= require that the static type match the dynamic one. // The following commented code is invalid, and will assert at runtime // const B& d=D(); // boost::optional_poly<B> z=d; // asserts, because optional_poly should be constructed with the concrete derived type // this is the correct way to initialize optional_poly. D must be derived from B (static assertion otherwise). boost::optional_poly<B> z=D(); std::cerr<<*z<<"\n"; // this prints 1 boost::optional_poly<B> s; // uninitialized (empty) optional_poly for interface B s.swap(z); // now s has a D, while z is empty std::cerr<<*s<<"\n"; // prints 1 return 0; } A bit more complex example: #include "optional_poly.hpp" #include <boost/none.hpp> #include <iostream> // we will show how simple callable objects can be returned easily and efficiently from a function (i.e. we implement a really small subset of boost::function) struct Callable { virtual void operator()() const=0; }; // inheritance is used here. This wrapper can host function pointers or functors seamlessly, exposing to the outside the Callable interface template<class T> struct MakeCallable: Callable { MakeCallable(const T& t) : val(t) {} T val; void operator()() const { val(); } }; void my_fun() { std::cout<<"got 1\n"; } struct MyFunc { void operator()() const { std::cout<<"got 2\n"; } }; // the function returning different Callable objects depending on the value of the parameter boost::optional_poly<Callable> foo(int i) { switch(i) { case 1: return MakeCallable<void (*)()>(my_fun); case 2: return MakeCallable<MyFunc>(MyFunc()); } return boost::none; } // main shows how foo() is invoked, and its result is used int main() { boost::optional_poly<Callable> c; for(int i=0;i<3; ++i) { c=foo(i); if(c) { std::cout<<"Going to call c for "<<i<<" ... ";(*c)(); } else { std::cout<<"empty c for "<<i<<"\n"; } } return 0; } Output: empty c for 0 Going to call c for 1 ... got 1 Going to call c for 2 ... got 2 Wasn't this easy? And this small example did not dynamically allocate any memory, because the Callable objects are all smaller than the small object threshold (default value is 128, can be overridden specifying a second template parameter to optional_poly, e.g. optional_poly<Callable,0> will always allocate from the heap). Sounds interesting for being submitted to boost? Corrado -- __________________________________________________________________________ dott. Corrado Zoccolo mailto: zoccolo@di.unipi.it PhD - Department of Computer Science - University of Pisa, Italy --------------------------------------------------------------------------