44

I'm using libgc, a garbage collector for C and C++. To make STL containers garbage collectible one must use the gc_allocator.

Instead of writing

std::vector<MyType> 

one has to write

std::vector<MyType,gc_allocator<MyType> >

Could there be a way to define something like

template<class T> typedef std::vector<T,gc_allocator<T> > gc_vector<T>;

I checked some time ago and found out it was not possible. But I may have been wrong or there might be another way around.

Defining maps in this way is particularly unpleasing.

std::map<Key,Val> 

becomes

std::map<Key,Val, std::less<Key>, gc_allocator< std::pair<const Key, Val> > >

EDIT: After trying the use of macro I found out the following code breaks it:

#define gc_vector(T) std::vector<T, gc_allocator<T> >
typedef gc_vector( std::pair< int, float > ) MyVector;

The comma inside the templated type definition is interpreted as a macro argument separator.

So it seems the inner class/struct is the best solution.

Here is an example on how it will be done in C++0X

// standard vector using my allocator
template<class T>
using gc_vector = std::vector<T, gc_allocator<T> >;

// allocates elements using My_alloc
gc_vector <double> fib = { 1, 2, 3, 5, 8, 13 };

// verbose and fib are of the same type
vector<int, gc_vector <int>> verbose = fib; 
chmike
  • 20,922
  • 21
  • 83
  • 106
  • why do you say that the `TypeHelper` solution requires you to redefine constructors? It's nothing more than a shortcut on a typedef... – Paolo Tedesco Mar 16 '09 at 11:21
  • Oops, you are right. It's good you clarified this in your answer. I was told that C++0X will provide a better solution by use of the 'using' keyword. Do you know how this would work ? – chmike Mar 16 '09 at 13:01
  • 1
    C++0x will provide template aliases, which will provide a way to do exactly what you want. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf – Luc Touraille Mar 16 '09 at 16:02
  • @WurmD which updated answer ? I changed the accepted answer to the one providing the C++11 method. Is that what you asked me ? – chmike Jun 27 '19 at 16:32

4 Answers4

94

You can use C++11 templated type aliasing using using e.g. like this

template <typename T>
using gc_vector = std::vector<T, gc_allocator<T>>;

Note: I know this is an old question but since it has quite many upvotes and as it turns up in search results I thought it deserved an updated answer.

Felix Glas
  • 15,065
  • 7
  • 53
  • 82
37

You cannot use a "templated typedef", but you can use a convenience class/struct with an inner type:

template<typename T>
struct TypeHelper{
    typedef std::vector<T,gc_allocator<T> > Vector;
};

and then use in your code

TypeHelper<MyType>::Vector v;
TypeHelper<MyType>::Vector::iterator it;

And something similar for the map:

template<typename K,typename V>
struct MapHelper{
    typedef std::map<K, V, gc_allocator<K,V> > Map;
};

EDIT - @Vijay: I don't know if there's another possible workaround, that's how I would do it; a macro might give you a more compact notation, but personally I wouldn't like it:

#define GCVECTOR(T) std::vector<T,gc_allocator<T> >

EDIT - @chmike: Please note that the TypeHelper solution does not require you to redefine constructors!

Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193
  • 1
    Yeap, there is. Inheritance is much neater is this very case. – sharptooth Mar 16 '09 at 09:49
  • 1
    @sharptooth: it's true that inheritance in this case gives you the best notation and avoids the use of a macro, but its usage is debatable, as pointed out in the comments to your answer. – Paolo Tedesco Mar 16 '09 at 09:56
  • I'm patiently waiting for any proof of potential problems. – sharptooth Mar 16 '09 at 10:02
  • I'll pick answer with the macro since it is the "best" solution. Both other solutions (inheritance or inner class/struct) require to redefine constructors. The macro would be used in typedef instructions. – chmike Mar 16 '09 at 11:00
  • @sharptooth: You've got proof already. "The standard doesn't guarantee that it'll behave as expected." It might work on every C++ compiler in existence today, but not in the version released next year. If you choose to rely on undefined behavior, that is your choice, but the burden of proof is yours – jalf Mar 16 '09 at 11:47
  • @jalf. Reasonable. Definitely from the "works, but proceed with care" department. – sharptooth Mar 16 '09 at 11:56
  • 2
    You could add in your answer that template typedefs (aka template aliases) will be included in C++0x (however, it's not supported by gcc yet) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf. – Luc Touraille Mar 16 '09 at 16:02
  • Thanks. I've added an example. I also show why the macro method is bogus and should not be used. Learned by experience. ;) – chmike Mar 16 '09 at 16:11
  • @Paolo: Useful for an unfortunate circumstance adapting another program's code. Thanks – Greg May 18 '12 at 20:45
  • I've inadvertently concocted a name for this pattern: [a metafactory](http://stackoverflow.com/q/19187469/1329652) :) – Kuba hasn't forgotten Monica Aug 19 '14 at 16:47
8

You can publicly inherit:

template<class T>
class gc_vector<T> : public std::vector<T, gc_allocator<T> >
{
    public:
    // You'll have to redeclare all std::vector's constructors here so that
    // they just pass arguments to corresponding constructors of std::vector
};

This solves your problem completely. The derived type can be used everywhere where the base type can be used, and there's no implementation overhead with any decent compiler.

The fact that std::vector has non-virtual destructor might lead to undefined behaviour according to C++ standard if you ever try to delete a derived class variable through a pointer to base class variable.

In real world this shouldn't matter in this particular case - the derived class has nothing new added compared to the base class and therefore the destructor for the derived class just calls the destructor for the base class. Proceed with paranoia, port carefully anyway.

If you never allocate this class variables on heap (and it's typical to allocate vector variables on stack and as members of other classes) the non-virtual destructor problem doesn't affect you.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • There is the problem that vectors are not designed to be derived from, as they lack a virtual destructor. –  Mar 16 '09 at 09:27
  • True, but it doesn't matter in this particular case, since the derived type has exactly the same set of meber variables as the base type. – sharptooth Mar 16 '09 at 09:33
  • It matters if he deletes a pointer to a base that points to a derived as he is then off in undefined behaviour land. –  Mar 16 '09 at 09:36
  • 3
    Nope, it doesn't matter in this case. The derived has no new data members compared to the base, so there's no difference between delete for Base* and Derived*. The derived constructor just calls the base constructor anyway. – sharptooth Mar 16 '09 at 09:42
  • hi sharptooth, what if he creates some memory in the derived constructor and deletes a pointer to a base that points to a derived class. Memory leak isnt it. I think, your solution fits perfectly for the current question, but its not robust. – Vijay Angelo Mar 16 '09 at 09:55
  • Also Sharptooth, please mention that...no fiddling in derived class, i.e no resource acquisition. I vote u up. – Vijay Angelo Mar 16 '09 at 09:57
  • 3
    @sharptooth - it does matter - what you suggest may happen, but the standard says it is UB. –  Mar 16 '09 at 09:58
  • 1
    Well, I hope that the standard is not the replacement for logic and common sense. Can you show an example of why this very case may cause problems? – sharptooth Mar 16 '09 at 10:01
  • What about constructors ? Regarding memory leaks, note that this is for a garbage collector use. As long as gc_allocator is used, there is no memory leak (assuming specification is correct :) ) – chmike Mar 16 '09 at 10:14
  • You'll have to redeclare all constructors so that they call corresponding vector's constructors. – sharptooth Mar 16 '09 at 10:22
  • 3
    There is no common sense in relying on undefined behavior. It's inherently unportable regardless of what one implementation happens to do. So yes, the standard is a replacement for "common sense" in this case. – Dan Olson Mar 16 '09 at 10:26
  • Yeap, I completely agree, but I'd like to see an example of what can go wrong in this particular case. – sharptooth Mar 16 '09 at 10:28
  • As long as nothing causes the gc_vector to have a vtable or any data, then this should fall into the 'not to standard, but you can certainly get away with it' category. If anything can cause gc_vector to have data or vtable, then deleting via base pointer is gonna kill you. – Michael Kohne Mar 16 '09 at 10:32
  • 1
    It's hard to give an example of undefined behavior. A conforming implementation could delete the contents of your hard disk if it felt like it. If you try it on all reasonable systems and it works as expected, feel free to use it knowing it's undefined. As a rule, though, I'd steer clear of UB. – Dan Olson Mar 16 '09 at 10:34
  • @Michael Not kill, just a memory leak in certain cases. – sharptooth Mar 16 '09 at 10:37
  • The need to redefine constructors (and I guess some typedefs too) in both solutions leads to the conclusion that the macro is the "best" solution. – chmike Mar 16 '09 at 10:46
  • Debugging code with macros is much harder compared to code with templates. – sharptooth Mar 16 '09 at 10:47
  • Macros are definitely not the best solution. Avoid themn. I'd go with the struct/typedef wrapper, and suffer the few characters extra typing, but in practice, sharptooths suggestion works too (although I'd only do it if I was sure I'd call the base class' destructor directly. – jalf Mar 16 '09 at 11:42
  • @sharptooth, no, he said kill, not memory leak, because that's what might happen. You can't in general be sure that the only function of a destructor is to release allocated memory. It might have important side effects. And so, it might corrupt the app state if the correct dtor is not called. – jalf Mar 16 '09 at 11:44
1

It can be done with a MACRO, if you're willing to push your compiler to its limits. I did it while implementing C++ equivalents for Java's "Future" and "Callable" classes. Our library uses reference-counted objects, so "Reference<T>" is itself a template class where "T" derives from "ReferencedObject".

1. Create your template Classes. Mine are:

    template<typename T>
    class Callable {
    private:

    public:
        virtual T Call() = 0;
    };

    template<typename T> CountedFuture : public ReferencedObject {
    private:
       Callable<T>* theTask;
       T            theResult;

    public:
       T Get() { 
          // Run task if necessary ...
          if(task) {
             theResult = theTask->Call();
             delete theTask;
          }
          return theResult;
       }
    };

2. In the application code I'm using references, so I define the macro:

   #define Future(T) Reference<CountedFuture<T>>

The beauty of this is that is that the Macro does exactly what you'd want from a "template typedef", the downsides being that you can't use "<>" for your type parameter(s) and there is no type inference.

3. I can now use the Macro wherever I would use a template, like in functions:

   Future(char*) DoSomething() { ... }
   bool          TestSomething(Future(std::string) f) { .... }
Zack Yezek
  • 201
  • 3
  • 2