1

Using Visual Studio 2010, I have:

using namespace std;
struct C 
{
    unique_ptr<F1, default_delete<F1>> Field1;
    unique_ptr<F2, default_delete<F1>> Field2;
    unique_ptr<FN, default_delete<F1>> FieldN;
}

It is going to be used in two contexts, CPU and GPU, in CPU context where the struct and the fields are going to have the default_delete and in the GPU context, with CUDA, where they are going to have a custom deleter which uses the function cudaFree to delete.

The custom deleter that might be used looks something like this

struct DevDeleter
{
    void operator()(void* d_ptr)
    {
        cudaError_t error = cudaFree(d_ptr);
        if (error != cudaSuccess)
        {
            throw;
        }
    }
}

So, my first hunch was to look at templating and my struct became:

template<typename Deleter> 
struct C 
{
    unique_ptr<F1, Deleter> Field1;
    unique_ptr<F2, Deleter> Field2;
    unique_ptr<FN, Deleter> FieldN;
}

I have a framework of structs (more than 30) that need to work in 2 delete contexts. If I want to declare struct C in some function, this will have a recursive declaration, which can't be written:

unique_ptr<C<default_delete<C<default_delete<C<(recursive)>>>>, default_delete<C(recursive)>> c(new C<...>());

Do you have an improvement or a clean solution to allow a struct to have custom unique_ptr deleter for its members?

N.B. I am aware that I can do template specialization, but that is effectively duplicating the struct.

Adam
  • 3,872
  • 6
  • 36
  • 66
  • 1
    Why is there a recursive declaration? The approach looks sane in principle. – Kerrek SB Apr 28 '13 at 19:54
  • because the default_delete requires a template which is the class name but the class name has a template and this template has the default_delete and so on... – Adam Apr 28 '13 at 19:56
  • 1
    I don't understand. You can say `C>`, or you can say `C`. Nothing recursive about that?! – Kerrek SB Apr 28 '13 at 20:02
  • In my client code I need to supply T and T in my case would be default_delete (again) and so on :):):). – Adam Apr 28 '13 at 20:05
  • Could you give a minimal complete example? – Vaughn Cato Apr 28 '13 at 20:07
  • Why do you need to supply `T`? I thought the type was `F`, and fixed? If not, make it a template argument. – Kerrek SB Apr 28 '13 at 20:07
  • @VaughnCato, this is a minimal and complete example, the code doesn't compile because it is a recursive template (if such a name exists). – Adam Apr 28 '13 at 20:17
  • It isn't complete, because no definition of F is provided, there is no main, etc. – Vaughn Cato Apr 28 '13 at 20:18
  • @VaughnCato the reason I called my struct and field C and F is to make it very generic, this is not really tied to my implementation, this is a generic problem. F is an empty struct and the recursive line is in my main function. – Adam Apr 28 '13 at 20:22
  • Why are you trying to pass `default_delete` instead of `default_delete`? – Vaughn Cato Apr 28 '13 at 20:26
  • @VaughnCato because EVERYTHING will run on the default_delete context OR the custom delete context, that is the structs and the fields which are in turn other structs. So, C and F, not just F, maybe I should have clarified this a bit more in the question. – Adam Apr 28 '13 at 20:28
  • `C` contains a pointer to an `F`. I don't see why you would be passing a `default_delete` when it is pointing to an `F`. Are you saying there are other situations where you want `C` to contain a pointer to a `C`? – Vaughn Cato Apr 28 '13 at 20:31
  • @VaughnCato, this is a problem where the whole struct will be instantiated again but with different deleter. So, if it is instantiated on the CPU, then the deleter is the default_delete, but if it is instantiated on the GPU, then the deleter is the custom one, so the way of declating the struct will differ as well. – Adam Apr 28 '13 at 20:40
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29067/discussion-between-vaughn-cato-and-adam) – Vaughn Cato Apr 28 '13 at 20:41

3 Answers3

2

I think this is what you want:

template <typename T>
struct DevDeleter
{
    void operator()(T* d_ptr)
    {
        cudaError_t error = cudaFree(d_ptr);
        if (error != cudaSuccess)
        {
            throw;
        }
    }
}

then you can have a template like this:

template<template <typename> class Deleter>
struct C 
{
    unique_ptr<F1, Deleter<F1>> Field1;
    unique_ptr<F2, Deleter<F2>> Field2;
    unique_ptr<FN, Deleter<FN>> FieldN;
}

and call it like this:

C c_default<default_delete>;

or this

C c_dev<DevDeleter>;
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • Thank you for the chat and thank you for giving me the exact answer! The trick was in the template declaration that you provided. – Adam Apr 28 '13 at 21:19
0

Do this:

template <typename T, typename D = std::default_deleter<T>> struct Foo
{
    std::unique_ptr<T, D> up;

    Foo(D const & d = D()) : up(nullptr, d) { }
};

Example:

// contains std::unique_ptr<int, std::default_deleter<int>>
Foo<int> x;

// contains std::unique_ptr<Bar, BarDeleter>
Foo<Bar, BarDeleter> y(BarDeleter(1, true, Blue));
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Can't use a constructor. And may struct contains many fields, not just one, while your solution makes T a field. I want the whole struct (of unknown number of fields) to be able to run on different contexts. – Adam Apr 28 '13 at 20:36
0

As an extension of @VaughCato's answer, consider making it:

template<template <typename> class Deleter = std::default_delete>
struct C 
{
    template <typename T> using unique_ptr = unique_ptr<T, Deleter<T>>;
    unique_ptr<F1> Field1;
    unique_ptr<F2> Field2;
    unique_ptr<FN> FieldN;
}

And the calls would be like so:

C<> c1;               // defaults to default_delete ...
C<default_delete> c2; // same thing but explicit
C<DevDeleter> c3;     // on device

H-o-w-e-v-e-r it seems a bit fishy that you have deletion logic "appended" through struct C rather than at the point where you create those unique pointers. It might be legit but I'd look into it.

einpoklum
  • 118,144
  • 57
  • 340
  • 684