Constructor-proxy: Template parameter deduction, a question for template masters

Hello to all, while developing Boost Shmem, I wanted to create a constructor proxy to emulate c++ dynamic object allocation syntax when creating shared memory objects using a proxy object in the following way: T * ptr = shmem_segment.create<T>("ObjectName")[size] /*proxy returned here, calls operator() of the object*/ (arg1, arg2, ...argN); Shared memory is allocated and the object is constructed. I want the arguments to be deduced automatically in the proxy object function call to preserve this syntax. I also want to avoid _all_ parameter copying. I mean, if I can deduce that an argument can be passed by value or by referenced using this syntax, great. But, between copying _all_ arguments and passing a reference in _all_, I prefer the second. Trying to find a correct proxy for this, I had many problems when passing temporary arguments and hardcoded constants to the proxy. Right now I am using some ugly casts to avoid the copies, but this has some side-effects. I will explain the problem in the following code: #include <new> //Object to be constructed struct Ctor2Arg { Ctor2Arg(const int &const_ref, int &non_const_ref){} }; //"Clean" proxy template <class T> struct constructor_proxy_t { template<class T0, class T1> void operator()(T0 & t0, T1 & t1) const { char m_mem[sizeof(T)]; //Call constructor and destructor T *ptr = new(m_mem)T(t0, t1); ptr->~T(); } }; //Ugly cast proxy template <class T> struct ugly_constructor_proxy_t { template<class T0, class T1> void operator()(const T0 & t0, const T1 & t1) const { char m_mem[sizeof(T)]; //Convert all refs to non-const refs T0 & t0ref = const_cast<T0 &>(t0); T1 & t1ref = const_cast<T1 &>(t1); //Call constructor and destructor, non-const //refs can be converted to const refs T *ptr = new(m_mem)T(t0ref, t1ref); ptr->~T(); } }; //Main function int main() { //Construct normal and ugly proxies constructor_proxy_t<Ctor2Arg> proxy; ugly_constructor_proxy_t<Ctor2Arg> ugly_proxy; //One const int and one non-const int const int dummy_const = 0; int dummy_non_const = 0; //This is ok, calls: //constructor_proxy_t<Ctor2Arg>::operator() //<int const ,int>(const int & t0=0, int & t1=0) proxy(dummy_const, dummy_non_const); //Both of these calls do not compile in VC7.1 or gcc 3.4.3, //tries to call constructor_proxy_t<Ctor2Arg>::operator() //<int, int>(int & t0=5, int & t1=0) // // gcc 3.4.3 error // //error: no match for call to // `(constructor_proxy_t<Ctor2Arg>) (int, int&)' //candidates are: // void constructor_proxy_t<T>::operator()(T0&, T1&) // [with T0 = int, T1= int, T = Ctor2Arg] // //Obviously, compilers are right, but //it wouldn't be more logical to call //<int const, int> specialization since a //temporary is a constant object? // proxy(dummy_non_const+5, dummy_non_const); // proxy(dummy_const+5, dummy_non_const); //This is ok, calls: //ugly_constructor_proxy_t<Ctor2Arg>::operator() //<int,int>(const int & t0=0, const int & t1=0) ugly_proxy(dummy_const, dummy_non_const); //These are ok, call: //ugly_constructor_proxy_t<Ctor2Arg>::operator() //<int,int>(const int & t0=5, const int & t1=0) ugly_proxy(dummy_non_const+5, dummy_non_const); ugly_proxy(dummy_const+5, dummy_non_const); //This compiles but it should not, since //I am passing a const object as second parameter //so Ctor2Arg obj(dummy_const, dummy_const) //does not compile. This calls: //ugly_constructor_proxy_t<Ctor2Arg>::operator() //<int,int>(const int & t0=0, const int & t1=0) ugly_proxy(dummy_const, dummy_const); return 0; } My question is, has anyone a better way to write the proxy object without ugly casts, no side-effects (I mean, the proxy call should compile only if the constructor compiles), preserving a clean calling syntax, and _no_ object copies at all? And the second question is: when a temporary is passed to a template, why non-const argument is deduced in the function call? Thank you, regards, /Ion

Hello Ion,
T * ptr = shmem_segment.create<T>("ObjectName")[size] /*proxy returned here, calls operator() of the object*/ (arg1, arg2, ...argN);
When reading your question (no answer for it, at least now) it come to my mind: - maybe there should be smart pointer for shmem and version of create() which returns smart pointer. It would result in greater safety of the library. Something as: // helper class, returned by segment::create(), // not to be used as is, only for conversion // to T* or to smart pointer template<typename T> class shmem_smart_pointer_proxy { shmem_smart_pointer_proxy(T* p_, segment* seg_) p(p_), seg(seg_) {} public: ~shmem_smart_pointer_proxy() { if (p) seg->destroy(p); } operator T*() { T* aux = p; p = 0; return aux; } // assigned to raw pointer private: T* p; segment* seg; }; // the smart pointer template<typename T> class shmem_smart_pointer { public: shmem_smart_pointer(const shmem_smart_pointer_proxy& aux) { p = aux.p; aux.p = 0; seg = aux.seg; } T* operator->() { ...} ~shmem_smart_pointer() { if (p) seg->destroy(p); } private: T* p; segment* seg; }; shmem_smart_pointer_proxy,T> = shmem_segment.create<T>("ObjectName")[size] (arg1, arg2, ...argN); It could be then used: T* t = segment.create(...); // no smarteness used shmem_smart_pointer ptr = segment_create(...); // smart ptr used (void)segment.create(...); // no leak happens but not as: segment.create(...)->do_something(); // this would probably be error anyway /Pavel

Hello Pavel,
T * ptr = shmem_segment.create<T>("ObjectName")[size]
It could be then used:
T* t = segment.create(...); // no smarteness used shmem_smart_pointer ptr = segment_create(...); // smart ptr used (void)segment.create(...); // no leak happens
The shared memory allocation is done in the proxy function, the example I've put was just to see the const_ref-non_const-ref problem so T* t = segment.create<T>("name"); --> T* t = segment.create<T>("name")/*proxy object returned*/; --> Is an invalid option and it does not compile, because that expresion returns a temporary proxy object not assignable to T*. To build an object without parameters, you should do: T* t = segment.create<T>("name")( ); --> T* t = segment.create<T>("name") /*proxy object returned, calling to operator()*/ ( /*empty param list*/); /*pointer returned*/ because the argument-less operator() is the responsible for allocating _and_ constructing the object, so there are no leaks. Obviously, this syntax is not very straight forward, and I should try to get more logical syntax when default constructing objects: T* t = segment.create<T>("name"); I will have a look at it, since I think this syntax is better, thank you for the idea. Regarding to const/non_const parameter issue, I will try to make a simpler example not involving proxies to just point out the problem and repost the question hoping there is response. Meanwhile, I keep on improving Shmem, currently adding boost unordered _map as name/memory mapping association instead of assoc_vector to test pros/cons. Regards, /Ion>
participants (3)
-
Ion GaztaƱaga
-
ION_G_M
-
Pavel Vozenilek