Выборочное выполнение кода без constexpr if, до современного C ++ - PullRequest
0 голосов
/ 20 сентября 2018

Я хочу написать некоторую многопоточную оболочку, и мне нужно работать с параметрами потока как void *, так как я использую библиотеку потоков не из C ++ 11.Я наткнулся на проблему и подготовил минимальный рабочий пример.Вот код:

#include <iostream>

namespace chops {
    template <typename T, typename U>
    struct is_same {
        static const bool value = false;
    };

    template<typename T>
    struct is_same<T, T> {
        static const bool value = true;
    };
}

template <typename Functor, typename T>
typename Functor::return_type fun_wrapper(T const& arg) {
    Functor f;
    typename Functor::return_type ret = f(arg);
    return ret;
}

struct hello_world {
    void operator()(int count) {
        while(count--)
            std::cout << "hello, world!\n";
    }
    typedef void return_type;
};

struct is_even {
    bool operator()(int x) {
        if(!(x % 2))
            return true;
        else
            return false;
    }
    typedef bool return_type;
};

int main() {
    //fun_wrapper<hello_world>(3);
    fun_wrapper<is_even>(3);
} 

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

void x = //something

Итак, я написал себе черту типа is_same и хочу выполнить этот код, только если Functor::return_type не является пустым.Я хочу получить эффект:

if constexpr(!is_same<Functor::return_type, void>::value) {
    typename Functor::return_type res = f(arg);
    return ret;
} else {
    f(arg);
}

Поскольку я не могу использовать современный C ++, но только C ++ 17, я не могу найти способ.Я не знаю, как добиться подобных эффектов с такими вещами, как SFINAE.

PS: обратите внимание, что это надуманный пример, и его можно использовать для работы с некоторыми изменениями, но в моей реальной программе мне нужно создать объект Functor::return_type, если он не является пустым.Поэтому, пожалуйста, не исключайте эту строку в своих ответах.Так что не просто использовать return f(arg).

Ответы [ 4 ]

0 голосов
/ 20 сентября 2018

Если вам не нужно хранить возвращаемое значение функтора, а только возвращать его самостоятельно, это работает в C ++ 03:

#include <iostream>

template <class Functor, class T>
typename Functor::return_type fun_wrapper(T const& arg) {
    return Functor()(arg);
}

struct hello_world {
    void operator()(int count) {
        while(count--)
            std::cout << "hello, world!\n";
    }
    typedef void return_type;
};

int main() {
    fun_wrapper<hello_world>(3);
} 

Если это решение, которое работает только для вашего MCVEтребуется явная специализация для void.

0 голосов
/ 20 сентября 2018

Вы можете сделать это путем частичной специализации шаблона класса над типом возвращаемого значения вашего функтора (R - это тип возвращаемого значения, и только если это void, компилятор выбирает специализированную версию):

template <typename R, typename Functor, typename T>
struct fun_wrapper_impl {
    static R wrap(T const& arg) {
        Functor f;
        R ret = f(arg);
        return ret;
    }
};

template <typename Functor, typename T>
struct fun_wrapper_impl<void, Functor, T> {
    static void wrap(T const& arg) {
        Functor f;
        f(arg);
    }
};

template <typename Functor, typename T>
typename Functor::return_type fun_wrapper(T const& arg) {
    return fun_wrapper_impl<typename Functor::return_type, Functor, T>::wrap(arg);
}

Godbolt с C ++ 98

Это позволяет вам специализировать поведение для любого конкретного типа.Если вместо этого вы предпочитаете использовать is_same (возможно, потому что в менее надуманном примере вы хотите добавить дополнительные сопоставления условий в ту же реализацию позже), вы также можете использовать диспетчеризацию тегов:

template <bool V>
struct tag {};

template <typename Functor, typename T>
typename Functor::return_type fun_wrapper_impl(T const& arg, tag<false>) {
    Functor f;
    typename Functor::return_type ret = f(arg);
    return ret;
}

template <typename Functor, typename T>
void fun_wrapper_impl(T const& arg, tag<true>) {
    Functor f;
    /* returns void */ f(arg);
}

template <typename Functor, typename T>
typename Functor::return_type fun_wrapper(T const& arg) {
    tag<chops::is_same<typename Functor::return_type, void>::value> the_tag;
    return fun_wrapper_impl<Functor, T>(arg, the_tag);
}

Годболт с C ++ 98

0 голосов
/ 20 сентября 2018

Довольно простое решение, основанное на двух приемах, которые использовались до "современного C ++":

  1. специализация шаблона

  2. Использование замены дляvoid если ничего не работает.

Пример:

#include <iostream>

template <typename VALUE>
struct Property {
  VALUE value;

  void set(VALUE value) { this->value = value; }
  const VALUE& get() const { return value; }
};

struct Void { };

template <>
struct Property<void>
{
  void set() { }
  Void get() { return Void(); }
};

std::ostream& operator<<(std::ostream &out, const Void &value)
{
  return out;
}

int main()
{
  Property<int> propInt;
  propInt.set(123);
  std::cout << propInt.get() << '\n';
  Property<void> propVoid;
  std::cout << propVoid.get() << '\n';
  return 0;
}

Вывод:

123

Live Demo на coliru

Обратите внимание, что я скомпилировал с -std=c++03, чтобы как можно лучше убедиться, что он будет работать и на не "современном" C ++.

0 голосов
/ 20 сентября 2018

Самый простой способ - создать две перегрузки: одна для случая, когда Functor::return_type равно void, а другая для случая, когда это не так.Все, что вам нужно сделать, это переопределить std::enable_if (например, в пространстве имен chops):

template <bool expr, typename T = void>
struct enable_if
{
};

template <typename T>
struct enable_if<true, T>
{
    typedef T type;
};

и затем заменить один шаблон функции fun_wrapper на два ограниченных:

template <typename Functor, typename T>
typename chops::enable_if<
    !chops::is_same<typename Functor::return_type, void>::value,
    typename Functor::return_type>::type 
fun_wrapper(T const& arg) {
    Functor f;
    typename Functor::return_type ret = f(arg);
    return ret;
}

template <typename Functor, typename T>
typename chops::enable_if<chops::is_same<typename Functor::return_type, void>::value>::type 
fun_wrapper(T const& arg) {
    Functor f;
    f(arg);
}

Демонстрация в реальном времени на wandbox .

...