Nano Hash - криптовалюты, майнинг, программирование

Как написать универсальную оболочку, не являющуюся членом, вокруг произвольных функций-членов?

Проблема

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

Для заданного 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...); }
      |              ^~~~~~~

Любые идеи?


Ответы:


1

Что касается 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 статический метод на метод экземпляра: 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

09.02.2020
  • Я сделал ошибку, намеревался взять адрес Helper::template wrapper<memberFunction> вместо Helper::template wrapper<R (T::*)(ARGS...)>; и да, поскольку memberFunction - это аргумент функции, я не могу использовать его здесь как параметр шаблона для staticWrapperHelper, хотя в общем случае указатели на функции могут быть параметрами шаблона. 11.02.2020
  • Теперь о вашем решении (спасибо!): Я скептически относился к необходимости сначала иметь здесь экземпляр, но кажется, что все это может быть constexpr, поэтому для меня это не проблема. Однако, хотя возвращаемый функтор может быть вызываемым, его нельзя использовать в качестве указателя на функцию. 11.02.2020
  • @haku Как вы заявили, параметры шаблона должны быть известны во время компиляции, поэтому указатель на функцию, который передается через аргумент функции, не может использоваться для параметра шаблона. Мне приходит в голову идея сделать функцию staticWrapper функцией constexpr, но я не уверен, что это может быть решением или нет. 12.02.2020
  • Я нашел способ, который принимает указатель функции и как параметр шаблона, и (позже) как параметр функции - последний, чтобы разбить подпись на класс, тип возвращаемого значения и аргументы. См. Мой ответ: stackoverflow.com/a/60225302/10614843 14.02.2020
  • @haku Хорошо. Похоже на ту же идею, которую я предложил выше; превращая фабричный метод в constexpr функцию. 14.02.2020

  • 2

    Я нашел решение, которое я могу принять, передав указатель функции-члена в качестве аргумента шаблона, который, к сожалению, требует, чтобы тип был явно упомянут в 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

    14.02.2020
  • В C ++ 17 у вас может быть template <auto ptm> 14.02.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..