Ограничить шаблон переменной списком типов - PullRequest
3 голосов
/ 01 ноября 2019

Я пытаюсь модернизировать код GStreamer , добавив умные указатели. Например,

GstElement *pipeline = gst_pipeline_new("test-pipeline");
gst_object_unref(pipeline);

можно переписать:

struct GstElementDeleter {
    void operator()(GstElement* p) { gst_object_unref(p); }
};

std::unique_ptr<GstElement, GstElementDeleter> pipeline = gst_pipeline_new("test-pipeline");

Но gst_object_unref() можно использовать для любого gpointer, поэтому его можно переписать:

template<typename T>
struct GPointerDeleter {
    void operator()(T* p) { gst_object_unref(p); }
};

std::unique_ptr<GstElement, GPointerDeleter<GstElement>> pipeline = gst_pipeline_new("test-pipeline");

Но я хотел бы ограничить это только обработкой типов, которые могут быть освобождены с помощью gst_object_unref. Есть ли способ объявить шаблон для работы только со списком типов - GstElement, GstBus и т. Д.

Ответы [ 2 ]

4 голосов
/ 01 ноября 2019

Возможно, вы могли бы сделать шаблон operator() (поэтому нет необходимости явно указывать параметр шаблона, определяющий умный указатель) и использовать SFINAE, чтобы включить operator() только для разрешенных типов

struct GPointerDeleter
 {
    template <typename T>
    typename std::enable_if<std::is_same<T, GstElement>::value
                         || std::is_same<T, GstBus>::value
                         /* or other cases */
             >::type operator() (T * p) const
     { gst_object_unref(p); }
 };

Или, может быть, лучше, вы можете добавить (как предложено Jarod42 (спасибо)) static_assert() проверку внутри operator()

struct GPointerDeleter
 {
    template <typename T>
    void operator() (T * p) const
     {
       static_assert( std::is_same<T, GstElement>::value
                   || std::is_same<T, GstBus>::value
                   /* or other cases */, "some error message" );

       gst_object_unref(p);
     }
 };
2 голосов
/ 01 ноября 2019

Возможно, черта типа? См. <type_traits>, если вы не видели их раньше.

template<typename T>
struct can_gst_unref : std::false_type { };
// for each type...
template<> struct can_gst_unref<GstElement> : std::true_type { };

// convenient alias, as is convention for type traits
template<typename T>
inline constexpr bool can_gst_unref_v = can_gst_unref<T>::value;

// now conditionally define operator() in your deleter
struct GstDeleter {
    template<typename T>
    std::enable_if_t<can_gst_unref_v<T>> operator()(T* p) { gst_object_unref(p); }
};

// Making the function a template instead of the class reduces clutter at usage
std::unique_ptr<GstElement, GstDeleter> works(gst_pipeline_new("test-pipeline"));

// can_gst_unref is not specialized to std::string
// so the general case takes over, and gives can_gst_unref_v<std::string> = false
// std::enable_if_t thus doesn't produce a type, and operator() is not defined, because it has no return type
// therefore, this doesn't compile
std::unique_ptr<std::string, GstDeleter> whoops;
...