3

What I aim to achieve is to have a templated Wrapper class with various aliases. Here is a simplified preview:

template < typename Type >
class Wrapper {
public:
   Wrapper(Type* resource) : ptr(resource) {}
   ~Wrapper() { free(ptr); }

private:
   Type* ptr;
}

void free(SDL_Window* ptr) { SDL_DestroyWindow(ptr); }
void free(SDL_Renderer* ptr) { SDL_DestroyRenderer(ptr); }

using Window = Wrapper<SDL_Window>;
using Renderer = Wrapper<SDL_Renderer>;

I would like to allow creation of only these aliased instances of Wrapper class. One of the reasons, is that this being wrapper to a SDL resource pointers, it has different memory freeing functions depending on type of the pointer.

Best scenario I would like to achieve would be to make Wrapper class not visible outside the usage of aliases I create. Maybe there is a solution using anonymous namespace, but thaw would mean wrapper class can't be in header file.

Maroš Beťko
  • 2,181
  • 2
  • 16
  • 42
  • 1
    Either put a static assert with a user-friendly message, or, define the class template's member functions in an implementation file, and implicitly instantiate only for the preferred subset of types – Piotr Skotnicki Mar 02 '17 at 13:37
  • I've also tried static_assert but I got an error that typeid(Type) == typeid(SDL_Window) isn't a constant expression – Maroš Beťko Mar 02 '17 at 13:44
  • 1
    You'd need `std::is_same::value`, or use some template machinery like boost mpl list or your custom implementation – Piotr Skotnicki Mar 02 '17 at 13:46
  • Ok so static_assert filled with is_same statements works. Still is there a way to hide Wrapper completely, while only seeing defined aliases? – Maroš Beťko Mar 02 '17 at 13:54
  • You need at least the declaration to be in a header file, and explicitly instantiate the class tempalte – Piotr Skotnicki Mar 02 '17 at 13:58

4 Answers4

4

Best scenario I would like to achieve would be to make Wrapper class not visible outside the usage of aliases I create.

This is possible using private and a wrapper class

class WrapperAccessor {
    template < typename Type >
    class Wrapper {
    public:
       Wrapper(Type* resource) : ptr(resource) {}
       ~Wrapper() { free(ptr); }

    private:
       Type* ptr;
    };

public:
    using Window = Wrapper<SDL_Window>;
    using Renderer = Wrapper<SDL_Renderer>;
};

using Window = WrapperAccessor::Window;
using Renderer = WrapperAccessor::Renderer;
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
1

How about std::enable_if, which will enable only certain types for your classes/functions?

Take a look at type_traits in C++11 and 14. You can do all kinds of static checks (checks at compile-time). For example, to check if the type is what you're expecting, you can use:

std::is_same<T,int>::value

And this will return true at compile time if T is int.

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • I would probably go with something similar to check for the allowed types. – Marco A. Mar 02 '17 at 13:28
  • So I've tried this, specified all the types it should allow and then created an Wrapper. It compiled and runs even though int specialization shouldn't be allowed at all, also no idea what happened in it's destructor because free(int) overload doesn't exist. – Maroš Beťko Mar 02 '17 at 13:32
  • @MarošBeťko If you don't want it to compile, then use `static_if`, instead of `if`. Here's how you implement it in C++11: http://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co – The Quantum Physicist Mar 02 '17 at 13:33
  • @MarošBeťko Also if you use `std::enable_if`, you can prevent compiling with types you don't want. But `enable_if` has some steep learning curve and you have to do it right. – The Quantum Physicist Mar 02 '17 at 13:34
0

I think this can help

template <typename T, bool Allowed>
class WrapperBase;


template <typename T>
class WrapperBase<T, true>
{
};

template <typename T>
class WrapperBase<T, false>;

template < typename Type >
class WrapperBaseHelper : public WrapperBase<T, boost::is_base_of<SDL_Window, T>::value | boost::is_base_of<SDL_Renderer>::value ...e.t.c >
{
...
};
Garf365
  • 3,619
  • 5
  • 29
  • 41
ArmanHunanyan
  • 905
  • 3
  • 11
  • 24
0

In your specific case, I would use std::unique_ptr, something like:

template <typename T, T value> struct Call; // c++17 should allow Call<auto F>

template <typename T, void (*f)(T*)>
struct Call<void (*)(T*), f>
{
    void operator ()(T* p) const { f(p); }
};

#define AUTO(F) decltype(F), F // not needed in C++17

using Window = std::unique_ptr<SDL_Window, Call<AUTO(&SDL_DestroyWindow)>>;
using Renderer = std::unique_ptr<SDL_Renderer, Call<AUTO(&SDL_DestroyRenderer)>>;

That handles deleted copy and correct move semantic contrary to your snippet.

Jarod42
  • 203,559
  • 14
  • 181
  • 302