Как написать обобщенную c не членную оболочку вокруг произвольных функций-членов? - PullRequest
0 голосов
/ 09 февраля 2020

Проблема

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

При условии bool std::string::empty(), обертка, которая будет сгенерирована будет bool wrapper(const std::string& str) { return str.empty(); }.

Функция-обертка не должна быть лямбда-выражением (чтобы ее можно было передавать как указатель на функцию).

Мой подход (для неконстантных функций-членов)

/* Wrapper (for non-const member functions) */

template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
    using fType = R (T::*)(ARGS...);
    template<fType FUNCTION>
    static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
};

template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
    //auto function = [memberFunction] (T& object, ARGS... args) -> R { return (object.*memberFunction)(args...); };
    using Helper = staticWrapperHelper<T, R, ARGS...>;

    R (*wrapper)(T&, ARGS...) = &Helper::template wrapper<R (T::*)(ARGS...)>;
    return wrapper;
}

/* Example */

class A
{
public:
    void member() {}
};

int main()
{
    A a;
    auto f = staticWrapper<A>(&A::member);
    f(a);

    return 0;
}

Проблема с моим подходом

Не компилируется. Похоже, что возвращаемая оболочка все еще является шаблоном: http://coliru.stacked-crooked.com/a/d4c0dd9ab631aa1f

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In instantiation of 'auto staticWrapper(R (T::*)(ARGS ...)) [with T = A; R = void; ARGS = {}]':
main.cpp:32:41:   required from here
main.cpp:17:9: error: no matches converting function 'wrapper' to type 'void (*)(class A&)'
   17 |     R (*wrapper)(T&, ARGS...) = &Helper::template wrapper;
      |         ^~~~~~~
main.cpp:8:14: note: candidate is: 'template static R staticWrapperHelper::wrapper(T&, ARGS ...) [with R (T::* FUNCTION)(ARGS ...) = FUNCTION; T = A; R = void; ARGS = {}]'
    8 |     static R wrapper(T& object, ARGS... args) { return object.*FUNCTION(args...); }
      |              ^~~~~~~

Есть идеи?

Ответы [ 2 ]

2 голосов
/ 09 февраля 2020

Что касается using fType = R (T::*)(ARGS...);, тип fType является типом указателя, поэтому его нельзя использовать в качестве параметра шаблона для запоминания / сохранения адреса метода экземпляра во время выполнения, который будет использоваться позже. Итак, вы должны определить элемент в staticWrapperHelper, чтобы сохранить указатель метода, который будет использоваться позже:

template<typename T, typename R, typename... ARGS>
struct staticWrapperHelper
{
    using fType = R (T::*)(ARGS...);
    fType method;
    R operator() (T& object, ARGS... args) { return (object.*method)(args...); }
};

Также я изменил метод wrapper stati c на метод экземпляра: operator() на быть объектом функтора.

Теперь в функции staticWrapper, которая работает как фабричная функция для staticWrapperHelper, мы возвращаем экземпляр staticWrapperHelper, в котором хранится указатель метода. Этот объект instace может быть использован позже для вызова метода экземпляра для предоставленного объекта типа T.

template<typename T, typename R, typename... ARGS>
auto staticWrapper(R (T::*memberFunction)(ARGS...))
{
    using Helper = staticWrapperHelper<T, R, ARGS...>;
    return Helper{.method = memberFunction};
}

Демонстрационная версия: http://coliru.stacked-crooked.com/a/9a7b7c04b36d9dc0

0 голосов
/ 14 февраля 2020

Я нашел решение, которое могу принять, передав указатель на функцию-член в качестве аргумента шаблона, что, к сожалению, требует явного упоминания типа в C ++ 14 (здесь может помочь вспомогательный макрос). Сигнатура указателя функции затем разбивается вспомогательной функцией для создания оболочки.

#include <iostream>

/* Wrapper (for non-const member functions) */

template<typename PTM, PTM ptm>
struct staticWrapper
{
    constexpr static auto make()
    {
        return breakDownAndWrap(ptm);
    }

private:
    template<typename C, typename R, typename... ARGS>
    constexpr static auto breakDownAndWrap(R (C::*)(ARGS...))
    {
        R (*wrapper)(C&, ARGS...) = &Wrapper<C, R, ARGS...>::get;
        return wrapper;
    }

    template<typename C, typename R, typename... ARGS>
    struct Wrapper
    {
        constexpr static R get(C& obj, ARGS... args)
        {
            return (obj.*ptm)(args...);
        }
    };
};

/* Optional helper macro */

#define AUTO_T(val) decltype(val), val

/* Example */

class A
{
public:
    int number;

    void member() { std::cout << number << std::endl; }
};

int main()
{
    A a {2};
    auto f = staticWrapper<decltype(&A::member), &A::member>::make();
    auto g = staticWrapper<AUTO_T(&A::member)>::make();
    f(a);
    g(a);

    return 0;
}

Рабочий пример: http://coliru.stacked-crooked.com/a/4c5cb1d98437c789

...