3

I am trying to write a generic allocator class that does not really release an object's memory when it is free()'d but holds it in a queue and returns a previously allocated object if a new one is requested. Now, what I can't wrap my head around is how to pass arguments to the object's constructor when using my allocator (at least without resorting to variadic templates, that is). The alloc() function i came up with looks like this:

template <typename... T>
inline T *alloc(const &T... args) {
    T *p;

    if (_free.empty()) {
        p = new T(args...);
    } else {
        p = _free.front();
        _free.pop();

        // to call the ctor of T, we need to first call its DTor
        p->~T();
        p = new( p ) T(args...);
    }
    return p;
}

Still, I need the code to be compatible with today's C++ (and older versions of GCC that do not support variadic templates). Is there any other way to go about passing an arbitrary amount of arguments to the objects constructor?

outis
  • 75,655
  • 22
  • 151
  • 221
rainer
  • 31
  • 2

2 Answers2

3

When you need to target pre-C++0x compilers you need to provide pseudo-variadic templates, i.e. you need to provide a template function for every needed arity:

template<class T> 
T* alloc() { 
    /* ... */ 
}

template<class T, class A0> 
T* alloc(const A0& a0) { 
    /* ... */ 
}

/* ... */

You can use preprocessor metaprogramming though to handle the repititions, e.g. by using Boost.Preprocessor or by simply generating the functions using a simple script.

Following is a simple example using Boost.PP:

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>

template<class T>
T* alloc() {
    return new T;
}

#define FUNCTION_ALLOC(z, N, _) \
  template<class T, BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), class T)> \
  T* alloc(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(N), const T, &p)) { \
     return new T( \
       BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(N), p) \
     ); \
  }

BOOST_PP_REPEAT(10, FUNCTION_ALLOC, ~)

#undef FUNCTION_ALLOC

This generates you alloc() template functions for up to 10 arguments.

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
  • Yep, Boost.PP helps - i think @GMan had a nice example somewhere, will try to dig it up. – Georg Fritzsche Apr 08 '10 at 14:10
  • I do plenty of Boost Preprocessor stuff, but I don't think I've put much on the site. Matthieu did one recently, though: http://stackoverflow.com/questions/2597586/simplifying-templates/2598283#2598283 – GManNickG Apr 08 '10 at 15:06
  • Didn't find what i had in mind, i probably confused it with something else. :) – Georg Fritzsche Apr 08 '10 at 15:07
1

The pre-C++11 solution to the problem is to provide only one simple alloc function which constructs a copy of its argument. This is the way C++03 allocators and all the containers worked, for more than 20 years. Applying it to your code it becomes:

template <typename T>
inline T *alloc(const &T arg) {
    T *p;

    if (_free.empty()) {
        p = new T(arg);
    } else {
        p = _free.front();
        _free.pop();

        // to call the ctor of T, we need to first call its DTor
        p->~T();
        p = new( p ) T(arg);
    }
    return p;
}

And then you call it as:

// copy construct T into the allocator's memory:
instance_of_your_allocator.alloc(T(1, 2, 3));

The downside of this approach is that it requires a copy-constructor to be available, and its potentially a costly operation.

One more example:

vector<T> vec;
vec.push_back(T(1, 2, 3)); // C++03 way, uses move cons-tor in C++11 if possible.
vec.emplace_back(1, 2, 3); // C++11 way, constructs in-place
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220