Любой тип функции-члена класса - PullRequest
0 голосов
/ 09 апреля 2020

Какова моя конечная цель?

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

class Foo {
private:
    int field_1_;
    double field_2_;

public:
    int field_1() const { return field_1_; }
    double field_2() const { return field_2_; }
};

std::map<std::string, any_mem_fn> fields = {
    { "field_1", &Foo::field_1 },
    { "field_2", &Foo::field_2 }
}; 

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

Что я пробовал?

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

class any_mem_fn {
    struct base_mem_fn_ptr {
        virtual ~base_mem_fn_ptr() {}
        virtual double operator()(Foo& foo) = 0;
    };

    template <typename F>
    struct mem_fn_ptr : base_mem_fn_ptr {
        F fn;

        mem_fn_ptr(F&& f)
            : fn(std::forward<F>(f))
        {}

        double operator()(Foo& foo) override {
            return std::invoke(fn, foo);
        }
    };

public:
    any_mem_fn() : fn_(nullptr) {}
    any_mem_fn(any_mem_fn&& rhs) = default;
    any_mem_fn(any_mem_fn const& rhs) = default;

    template <typename F>
    any_mem_fn(F&& f) {
        fn_ = std::make_shared<mem_fn_ptr<F>>(std::forward<F>(f));
    }

    any_mem_fn& operator=(any_mem_fn&& rhs) = default;
    any_mem_fn& operator=(any_mem_fn const& rhs) = default;

    ~any_mem_fn() = default;

    template <typename T>
    double value(T&& obj) {
        return std::invoke(*fn_, obj);
    }

private:
    std::shared_ptr<base_mem_fn_ptr> fn_;
};

Здесь, как вы можете видеть, я использовал полиморфизм для стирания типа. Кроме того, вы можете заметить, что operator() в случае каждой сигнатуры функции возвращает double. Это преднамеренное , и в настоящее время это не моя проблема, поэтому, пожалуйста, не обращайтесь к этому.

Теперь, в чем проблема с вышеуказанным классом ... Проблема в том, что он работает только для функции-члены класса Foo. Если я попытаюсь сохранить функцию-член класса Bar, приведенный выше код не будет работать. Для того чтобы приведенный выше код работал в случае любого класса, мне нужно было бы перегрузить operator() для каждого указанного класса c, что я не могу сделать, потому что моя библиотека не знает обо всех классах, которые есть у пользователя библиотеки.

Полная демонстрация: здесь .

Библиотека, над которой я работаю, здесь .

Вопрос: Можно ли каким-то образом сохранить какую-либо функцию-член класса в классе и иметь возможность вызывать эту функцию-член позже для объекта?

1 Ответ

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

Не уверен, что это то, что вам нужно, но с std :: function и std :: any вы можете сделать что-то вроде:

class any_mem_fn {
public:
    template <typename Ret, typename C>
    any_mem_fn(Ret (C::*m)()) {
        fn_ = [m](std::any a) { return (std::any_cast<C>(a).*m)(); };
    }
    // Possibly provide overloads for combination const/volatile, reference...

    template <typename T>
    double value(T obj) { return fn_(obj); }

private:
    std::function<double(std::any)> fn_;
};

Demo

...