Двоичная функция обратной тяги

Я пытаюсь определить функцию, которая будет возвращать оператор желаемого типа на основе содержимого строки. Я пробовал это, но это не работает:

импл.cpp

template <typename T> thrust::binary_function<T,T,bool>
 get_filter_operator(const std::string &op)
    if (op == "!=")
        return thrust::not_equal_to<T>();
    else if (op == ">")
        return thrust::greater<T>();
    else if (op == "<")
        return thrust::less<T>();
    else if (op == ">=")
        return thrust::greater_equal<T>();
    else if (op == "<=")
        return thrust::less_equal<T>();
    else
    {
        return thrust::equal_to<T>();
    }

template thrust::binary_function<float,float,bool> get_filter_operator<float>(const std::string &);

импл.ч

template <typename T> thrust::binary_function<T, T, bool> get_filter_operator(const std::string &op);

Как вернуть указатель на произвольную функцию, например thrust::not_equal_to<int>() или thrust::equal_to<int>()? Я не могу найти правильный тип для возврата.

ИЗМЕНИТЬ

Как и просили, ошибка компилятора:

В экземпляре ‘thrust::binary_function‹T, T, bool> get_filter_operator(const string&) [with T = float; std::string = std::basic_string‹char>]’:

ошибка: не удалось преобразовать «thrust::equal_to‹float>()» из «thrust::equal_to‹float>» в «thrust::binary_function‹float, float, bool>»

Обновить

Хорошо, извините, что не упомянул об этом раньше: проблема в том, что я не могу использовать std::function, потому что он будет работать только с кодом хоста. Я хотел использовать бинарные функции тяги, чтобы я мог использовать их как в GPU, так и в CPU.


person gumlym    schedule 03.02.2016    source источник
comment
Я могу показать вам приближение к этому с помощью функторов, но на самом деле это не ответ на ваш вопрос.   -  person Robert Crovella    schedule 04.02.2016
comment
Я думаю, что самый простой способ получить то, что вы хотите, - это написать функтор, который выполняет switch над желаемым оператором фильтра. Я не думаю, что thrust:not_equal_to и друзья здесь сильно помогают.   -  person Jared Hoberock    schedule 05.02.2016
comment
Собственно, я так и сделал. Я хотел знать, есть ли более элегантный способ   -  person gumlym    schedule 05.02.2016


Ответы (3)


Как я могу вернуть указатель на произвольную функцию, такую ​​как тяга::not_equal_to() или тяга::equal_to()? Я не могу найти правильный тип для возврата

Каждая из вещей, которые вы пытаетесь вернуть, является функцией двух аргументов, каждый из которых имеет некоторый тип T, который возвращает bool. Правильный тип возврата

std::function<bool(T, T)>

As in:

#include <thrust/functional.h>
#include <functional>
#include <string>

template<typename T>
std::function<bool(T, T)>
get_filter_operator(const std::string &op)
{
    if (op == "!=")
        return thrust::not_equal_to<T>();
    else if (op == ">")
        return thrust::greater<T>();
    else if (op == "<")
        return thrust::less<T>();
    else if (op == ">=")
        return thrust::greater_equal<T>();
    else if (op == "<=")
        return thrust::less_equal<T>();
    else
    {
        return thrust::equal_to<T>();
    }
}

#include <iostream>

using namespace std;

int main()
{
    auto relop = get_filter_operator<int>("!=");
    cout << boolalpha << relop(1,0) << endl;
    cout << boolalpha << relop(1,1) << endl;

    return 0;
}

Теперь вы можете повторить свой комментарий к @MohamadElghavi:

Да, я знал, что это сработало, но проблема в том, что я пытаюсь вернуть тягу :: бинарную_функцию, а не из стандартного

Это может быть то, что вы пытаетесь сделать, но пытаться делать это неправильно и невозможно. Посмотрите определение template<typename A1, typename A2, typename R> struct thrust::binary_function в <thrust/functional> и соответствующую документацию. Примечание:

binary_function — это пустой базовый класс: он не содержит функций-членов или переменных-членов, а только информацию о типах.

В частности, у thrust::binary_function<A1,A2,R> нет operator(). Это не вызывается. Он не может хранить какой-либо другой вызываемый объект (или вообще что-либо). См. также определения equal_to, not_equal_to и т. д. в том же файле. binary_function не является даже основанием ни для одного из них. Ни один из них не конвертируется в binary_function.

Обратите внимание:

двоичная_функция в настоящее время является избыточной с типом C++ STL std::binary_function. Мы резервируем его здесь для потенциальной дополнительной функциональности на более поздний срок.

(std::binary_function сам по себе устарел с C++11 и будет удален в C++17).

thrust::binary_function<T,T,bool> это не то, что вы ищете. std::function<bool(T, T)> есть.

std::function<bool(int, int)> f = thrust::greater<int>(); 

заставляет f инкапсулировать вызываемый объект, который является thrust::greater<int>

Позже

Проблема в том, что его можно использовать только в хост-коде, не так ли? Прелесть бинарных функций тяги в том, что их можно использовать как в GPU, так и в CPU.

Я думаю, у вас может сложиться впечатление, что, например.

std::function<bool(int, int)> f = thrust::greater<int>();  /*A*/

берет thrust::greater<int> и каким-то образом понижает его до std::function<bool(int, int)>, который имеет аналогичные, но более ограниченные ("стандартные") возможности выполнения.

Ничего подобного. std::function<bool(int, int)> foo — это просто вместилище для всего, что bar вызывается с двумя аргументами, неявно преобразуемыми в int, и возвращает нечто, неявно преобразуемое в bool, например, если:

std::function<bool(int, int)> foo = bar; 

затем, когда вы вызываете foo(i,j), вам возвращается результат, как bool, выполнения bar(i,j). Не результат выполнения чего-либо, что хоть как-то отличается от bar(i,j).

Таким образом, в /*A*/ выше вызываемая вещь, содержащаяся и вызываемая f, является двоичной функцией тяги; это это thrust::greater<int>(). Метод, вызываемый методом operator() f, является thrust::greater<int>::operator().

Вот программа:

#include <thrust/functional.h>
#include <functional>
#include <iostream>

using namespace std;

int main()
{
    auto thrust_greater_than_int = thrust::greater<int>();
    std::function<bool(int, int)> f = thrust_greater_than_int;
    cout << "f " 
        << (f.target<thrust::greater<int>>() ? "calls" : "does not call") 
        << " a thrust::greater<int>" << endl;
    cout << "f " 
        << (f.target<thrust::equal_to<int>>() ? "calls" : "does not call") 
        << " a thrust::equal_to<int>" << endl;
    cout << "f " 
        << (f.target<std::greater<int>>() ? "calls" : "does not call") 
        << " an std::greater<int>" << endl;
    cout << "f " 
        << (f.target<std::function<bool(int,int)>>() ? "calls" : "does not call") 
        << " an std::function<bool(int,int)>" << endl;
    return 0;
}

который сохраняет thrust::greater<int> в std::function<bool(int, int)> f, а затем сообщает вам, что:

f calls a thrust::greater<int>
f does not call a thrust::equal_to<int>
f does not call an std::greater<int>
f does not call an std::function<bool(int,int)>
person Mike Kinghan    schedule 03.02.2016
comment
Проблема в том, что его можно использовать только в хост-коде, не так ли? Прелесть бинарных функций тяги в том, что их можно использовать как в GPU, так и в CPU. В противном случае я бы просто использовал стандартные функции и вообще не использовал тягу. - person gumlym; 04.02.2016
comment
@gumlyn Обновление не поможет. - person Mike Kinghan; 05.02.2016
comment
хорошо, извините, что не упомянул об этом раньше, я не подумал об этом - person gumlym; 05.02.2016

Несмотря на то, что точного ответа не было, я собираюсь разместить здесь то, что в итоге использовал, на случай, если кому-то понадобится что-то подобное.

В файле .cuh

#include <cuda.h>
#include <cuda_runtime_api.h>

namespace BinaryFunction
{
    enum class ComparisonOperator
    {
      equal_to,
      not_equal_to,
      greater,
      less,
      greater_equal,
      less_equal
    };

    enum class BitwiseOperator
    {
      bit_and,
      bit_or
    };

    template<typename T>
    struct CompareFunction
    {
      __host__ __device__ T operator()(const T &lhs, const T &rhs, ComparisonOperator &op) const
      {
            switch (op)
            {
                case ComparisonOperator::equal_to:
                    return lhs==rhs;
                case ComparisonOperator::not_equal_to:
                    return lhs!=rhs;
                case ComparisonOperator::greater:
                    return lhs>rhs;
                case ComparisonOperator::less:
                    return lhs<rhs;
                case ComparisonOperator::greater_equal:
                    return lhs>=rhs;
                case ComparisonOperator::less_equal:
                    return lhs<=rhs;

            }
        }
    };

    template<typename T>
    struct BitwiseFunction
    {
      __host__ __device__ T operator()(const T &lhs, const T &rhs, BitwiseOperator &op) const
      {
        if (op==BitwiseOperator::bit_and)
          return lhs & rhs;
        else if (op==BitwiseOperator::bit_or)
          return lhs | rhs;
      }
    };
}

Затем используйте его следующим образом: В файле cpp:

BinaryFunction::ComparisonOperator comp_op = BinaryFunction::ComparisonOperator::equal_to;

BinaryFunction::CompareFunction<int> comp_func;

А потом в ядре или в обычной функции:

int value_a;
int value_b;
comp_func(value_a, value_b, comp_op)
person gumlym    schedule 12.02.2016
comment
Разве это не влияет на производительность, если выполнять переключение для каждого значения внутри ядра, а не перед вызовом ядра? - person Robear; 30.10.2018

Следующее работает для меня.

#include <iostream>
#include <functional>

template<class T>
std::function<bool(T, T)> GetOperator(const std::string& op)
{
    if (op == "!=")
        return std::not_equal_to<T>();
    else if (op == ">")
        return std::greater<T>();
    else if (op == "<")
        return std::less<T>();
    else if (op == ">=")
        return std::greater_equal<T>();
    else if (op == "<=")
        return std::less_equal<T>();
    else
    {
        return std::equal_to<T>();
    }
}

int main()
{
    auto op = GetOperator<int>(">");
    std::cout << op(1, 2) << '\n';
    return 0;
}
person Mohamad Elghawi    schedule 03.02.2016
comment
Да, я знал, что это сработало, но проблема в том, что я пытаюсь вернуть тягу :: бинарную_функцию, а не из стандартного - person gumlym; 03.02.2016
comment
@gumlym Какие ошибки вы получаете? Я никогда раньше не использовал тягу, но беглый взгляд на документы показывает, что ваш код должен работать (кроме отсутствующих фигурных скобок). - person Mohamad Elghawi; 03.02.2016