Как привести "void (MyClass :: *) (int)" к "void (*) (int)"? - PullRequest
1 голос
/ 11 апреля 2020

Я искал способ приведения члена класса к обратному вызову C.

Недавно я нашел ответ со специальным хаком bind, позволяющим привязать членов класса к обратным вызовам C:

{ ссылка }

У меня есть этот рабочий код для привязки функции MyClass :: f к C функции f : Но я хочу избежать явной передачи cb_type в качестве параметра шаблона в функцию c_bind. В приведенном примере CB имеет тип void (*) (int) и Fun c параметр шаблона имеет void (MyClass :: *) ( int) type.

template<typename CB, typename Func, typename... Params>
CB* c_bind(std::_Bind<Func(Params...)> function) {
    return Callback<typename ActualType<CB>::type, __COUNTER__, Func>::getCallback(function);
}

typedef void (cb_type)(int);

class MyClass {
public:
    void f(int x) {
        std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
    }
};

int main() {
    MyClass mc;

    auto f = c_bind<cb_type>(std::bind(&MyClass::f, mc, std::placeholders::_1));
    //                ^ how to avoid explicit callback type declaration here?
    f(10);

    return 0;
}

Также я нашел этот фрагмент кода (https://gist.github.com/vikchopde/73b62314379f733e8938f11b246df49c) для "развертывания" некоторых функций.

bool ok = fu::is_unwrappable<decltype(&MyClass::f)>::value; // always false
// fu::unwrap_function<decltype(&MyClass::f)>::type::function_ptr blah; // won't compile

но это не сработает по неизвестной мне причине.

Мой вопрос, есть ли обходной путь для извлечения возвращаемого типа и списка аргументов из типа с указателем класса-члена, например void (MyClass :: * ) (int) и contruct C -подобного типа void (*) (int) ?

Спасибо за любую помощь!

Ответы [ 2 ]

1 голос
/ 11 апреля 2020

Ну, в C ++ 17 вам разрешено передавать произвольный параметр не-типа в класс с template<auto>. Поэтому мы можем сохранить MyClass::f в качестве параметра шаблона и проанализировать его тип с помощью decltype. После передачи этого типа в другой шаблонный класс мы можем извлекать нужные типы, используя специализацию шаблонов.

Приведенный ниже код показывает, как создать функцию C в стиле wrapper<>::func_type.

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

class MyClass {
public:
    void f(int x) {
    std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
    }
};

void f(int x) {
    std::cout << "Hello from f(int), value: " << x << std::endl;
}

template<auto F>
struct wrapper
{
    template<typename> struct inner;

    template<class Cls, typename Ret, typename... Args>
    struct inner<Ret(Cls::*)(Args...)>
    {
        using func_type = Ret(Args...);

        static auto bind(Cls *obj)
        {
            return [=](Args ...args){
                return (obj->*F)(std::forward<Args>(args)...);
            };
        }
    };

    using func_type = typename inner<decltype(F)>::func_type;
    static const constexpr auto bind = inner<decltype(F)>::bind;
};

int main() {
    MyClass mc;

    auto h = wrapper<&MyClass::f>::bind(&mc);
    h(10);

    using func_t = typename wrapper<&MyClass::f>::func_type;
    std::function<func_t> g = f;
    g(1);
    return 0;
}
0 голосов
/ 11 апреля 2020

Прежде всего, я хотел бы поблагодарить @Dappur за хороший пример. Используя ваше руководство, я перепишу свой уродливый интерфейс bind с использованием std :: _ Bind позже. Также я хочу поблагодарить @Sam Varshavchik за упоминание этого набора книг по C ++. Я начну читать его, чтобы стать таким же гроссмейстером C ++, как вы, чтобы узнать, как " почему я не могу разыграть его вот так ". Но, к сожалению, с моим плохим опытом в C ++ я все еще могу сделать это сейчас. Вот рабочий код:

template<class T, unsigned int n, class CallerType>
struct CallbackWrapper;

template<class Ret, class... Params, unsigned int n, class CallerType>
struct CallbackWrapper<Ret(Params...), n, CallerType> {
    static auto get(std::function<Ret(Params...)>&& fn) -> Ret(*)(Params...) {
        func = fn;
        return static_cast<Ret(*)(Params...)>(CallbackWrapper<Ret(Params...), n, CallerType>::callback);
    }

private:
    static std::function<Ret(Params...)> func;

    static Ret callback(Params... args) {
        return func(args...);
    }
};

template<class Ret, class... Params, unsigned int n, class CallerType>
std::function<Ret(Params...)> CallbackWrapper<Ret(Params...), n, CallerType>::func;

template<typename T>
struct lambda_to_stdf {
    using type = void;
};

template<typename Ret, typename Class, typename... Args>
struct lambda_to_stdf<Ret(Class::*)(Args...) const> {
    using type = std::function<Ret(Args...)>;
};

template<class Ret, class Cls, class... Args1, class... Args2>
auto c_bind(std::_Bind<Ret(Cls::*(Cls, Args1...))(Args2...)> function) -> Ret(*)(Args2...) {
    return CallbackWrapper<Ret(Args2...), __COUNTER__, Ret(Cls::*(Cls, Args1...))(Args2...)>::get(std::move(function));
}

template<class Ret, class... Args>
auto c_bind(std::function<Ret(Args...)> function) -> Ret(*)(Args...) {
    return CallbackWrapper<Ret(Args...), __COUNTER__, std::function<Ret(Args...)>>::get(std::move(function));
}

template<class F>
auto c_bind(F function) -> decltype(c_bind((typename lambda_to_stdf<decltype(&F::operator())>::type)(function))) {
    return c_bind((typename lambda_to_stdf<decltype(&F::operator())>::type)(function));
}

Использование:

class MyClass {
public:
    void f(int x) {
        std::cout << "Hello from MyClass::f(int), value: " << x << std::endl;
    }
};

int main() {
    MyClass mc;

    auto f = c_bind(std::bind(&MyClass::f, mc, std::placeholders::_1));
    f(10);

    std::function<void(int)> stdf = [](int v) {
        std::cout << "hello from std::function, value: " << v << std::endl;
    };
    auto f2 = c_bind(stdf);
    f2(100);

    auto f3 = c_bind([](int v) -> int {
        std::cout << "hello from lambda, value: " << v << std::endl;
        return 5.0f;
    });
    f3(1000);

    return 0;
}

Надеюсь, это кому-нибудь пригодится.

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