Как передать предикат свободной формы в качестве аргумента указателя на функцию шаблона? - PullRequest
0 голосов
/ 08 декабря 2018

Я пытаюсь написать функцию, которая создает битовую маску путем тестирования последовательности объектов с предикатом произвольной формы:

template<typename... Args, bool(*TestingFunc)(Object, Args...)>
inline uint32_t getMask(const std::vector<Object>& objects, Args... args)
{
    uint32_t mask = 0;

    for(size_t i = 0; i < objects.size(); i++)
        if(TestingFunc(objects[i], args...))
            mask |= 1 << i;

    return mask;
}

Однако приведенный выше код не работает: gcc / clang reportno matching function for call to при каждом вызове функции (note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Args' с помощью clang, note: template argument deduction/substitution failed: с помощью gcc; вызов getMask<int, pred1>(objects, 10), предикат bool pred1(Object, int), см. информацию ниже).

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

/* bool predicate1(Object, size_t, size_t, int, int); */
mask = getMask<predicate1>(objects, a.height(), b.width(), 10, 15);
/* ... */
/* bool predicate2(Object, int, int); */
mask = getMask<predicate2>(objects, x, y);

Это горячая точка в моей программе;производительность имеет решающее значение.getMask() и предикаты помечены встроенными.Код должен быть написан на C ++ 11 (не на C ++ 14 или выше).

Итак, как следует писать getMask()?

Ответы [ 2 ]

0 голосов
/ 08 декабря 2018

Я предлагаю вам следовать стандартной библиотеке и просто передавать вызываемый шаблонного типа по значению.Если это не указатель на функцию, он будет встроен.

template<class... Args, class F>
inline uint32_t getMask(F f, const std::vector<Object>& objects, Args... args)
{
    uint32_t mask = 0;

    for(size_t i = 0; i < objects.size(); i++)
        if(f(objects[i], args...))
            mask |= 1 << i;

    return mask;
}

И есть очень простой способ преобразовать указатель на функцию без вызова состояния, если это ctce:
Использоватьstd::integral_constant<decltype(p), p>.В C ++ 17, возможно, с помощником.

template <auto f>
using to_type = std::integral_constant<decltype(f), f>;

getMask(to_type<checkit>(), somedata, arg0, arg1, arg2);
getMask([](auto... x){ return checkit(x...); }, somedata, arg0, arg1, arg2);
0 голосов
/ 08 декабря 2018

С c ++ 17 вы можете написать

template<auto TestingFunc, typename... Args>
inline uint32_t getMask(const std::vector<Object>& objects, Args... args)
{
    uint32_t mask = 0;

    for(size_t i = 0; i < objects.size(); i++)
        if(TestingFunc(objects[i], args...))
            mask |= 1 << i;

    return mask;
}

В C ++ 11 вам нужно гораздо больше:

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

struct Object
{   
    Object( int _val ):val{_val}{}
    int val;
};  

template <class F, F f> struct caller;

template <class Obj, class... Args, bool (* TestingFunc)(Obj, Args...)>
struct caller<bool (*)(Obj, Args...), TestingFunc>
{   
    static uint32_t Do( std::vector<Object>& objects, Args... args) 
    {
        uint32_t mask = 0;

        for(size_t i = 0; i < objects.size(); i++)
            if(TestingFunc(objects[i], args...))
                mask |= 1 << i;

        return mask;
    }
};  

bool checkit( const Object& o, int i ) 
{   
    return o.val==i;
}   

int main()
{   
    std::vector<Object> objects;
    objects.emplace_back(1);
    objects.emplace_back(2);
    objects.emplace_back(3);

// The ugly thing before c++17 is, that you have to specify the
// function type itself as a template parameter. As said: C++17
// has offered exactly the feature of auto here in template parameter lists
// to allow deduction of type from parameter.
    std::cout << caller<decltype(&checkit), checkit>::Do(objects,3) << std::endl;
}

Но я считаю, что трудоемкая вещь вовсе не заключается в использовании указателя на функцию.Прежде чем вручную взломать много вещей, измерить, измерить, измерить!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...