Replace pointer function implementation with std :: function to use lambdas in PlayFab SDK

I'm currently trying to include the PlayFab C ++ SDK in my application. This SDK is mainly intended for the Cocos2d-x game engine, but can be used for any C ++ application.

This is just REST, so you send requests to your servers and wait for a response. That would be ideal for using lambda.

They declare this callback, which is called when the request is successful:

template<typename ResType> using ProcessApiCallback = void(*)(const ResType& result, void* userData);

Unfortunately, they do not use std :: function, but a function pointer. Therefore, you cannot use lambdas that capture variables.

Therefore, I thought I could just replace the function callback with the std :: function callback as follows:

template<typename ResType> using ProcessApiCallback = std::function<void(const ResType& result, void* userData)>;

Unfortunately, it's not just that they store function pointers using the ugly reinterpret_casts, here is an example (remove unnecessary parts so that it is short):

void PlayFabClientAPI::LoginWithAndroidDeviceID(
    LoginWithAndroidDeviceIDRequest& request,
    ProcessApiCallback<LoginResult> callback,
    ErrorCallback errorCallback,
    void* userData
    )
{
    // here they assign the callback to the httpRequest
    httpRequest->SetResultCallback(reinterpret_cast<void*>(callback));
    httpRequest->SetErrorCallback(errorCallback);
    httpRequest->SetUserData(userData);

    PlayFabSettings::httpRequester->AddRequest(httpRequest, OnLoginWithAndroidDeviceIDResult, userData);
}

Later, when the request was successful, they do the following:

if (request->GetResultCallback() != nullptr)
{
    ProcessApiCallback<LoginResult> successCallback = reinterpret_cast<ProcessApiCallback<LoginResult>>(request->GetResultCallback());
    successCallback(outResult, request->GetUserData());
}

The HttpRequest class has this field:

void* mResultCallback;

The problem is that I don’t know how to store arbitrary std :: function pointers in the HttpRequest class and then discard them later. I tried a lot of things, including the very ugly reinterpret_casting, but nothing worked.

I am open to any changes to their SDK. I also reported this as a bad design, and they agreed, but they don’t have time to improve it, but they will accept the pull request if a good solution can be found.

+4
3

userData. , . , , , .

.

- , C. API : . , , .

.

++, C- . .

, , std::function, - , std::function, , , .

reinterpret_cast , , delete ( , ), ( , std::function - , ).

, ++, , C, , C. , , ++, , reintepret_cast. .

+1

@SamVarshavchik , , userData " " userData :

#include <memory>
#include <utility>
#include <type_traits>
#include <iostream>

template <typename T, typename... Args>
void c_callback_adaptor(Args... args, void* userData) {
    auto callable = reinterpret_cast<T*>(userData);
    (*callable)(args...);
}

template <typename Fn>
struct genCCallback_impl;
template <typename Res, typename... Args>
struct genCCallback_impl<Res(Args...)> {
    template <typename T>
    static std::pair<std::unique_ptr<std::decay_t<T>>,
                     Res (*)(Args..., void*)>
    gen(T&& callable) {
        return std::make_pair(
            std::make_unique<std::decay_t<T>>(std::forward<T>(callable)),
            c_callback_adaptor<std::decay_t<T>, Args...>);
    }
};

template <typename Fn, typename T>
auto genCCallback(T&& callable) {
    return genCCallback_impl<Fn>::gen(std::forward<T>(callable));
}

:

extern void registerCallback(void (*callbackFn)(int, int, void*), void* userData);

void test(int n) {
    auto cCallback = genCCallback<void(int, int)>(
        [n](int x, int y) {
            std::cout << n << ' ' << x << ' ' << y << '\n';
        });
    registerCallback(cCallback.second, cCallback.first.get());
    // for demo purposes make the allocated memory permanent
    (void) cCallback.first.release();
}

(, std::unique_ptr, .)

: , , , __lambda1. test() , :

void c_callback_adaptor_lambda1(int x, int y, void* userData) {
    auto callable = reinterpret_cast<__lambda1*>(userData);
    (*callable)(x, y);
}

class __lambda1 {
public:
    __lambda1(int n) { ... }
    void operator()(int x, int y) const { std::cout << ...; }
    ...
private: ...
};

void test(int n) {
    auto cCallback = std::make_pair(
        std::make_unique<__lambda1>(n),
        c_callback_adaptor_lambda1);
    registerCallback(cCallback.second, cCallback.first.get());
    (void) cCallback.first.release();
}
+1

PlayFab Cocos2dxSDK SDK.

:

PlayFabClientAPI::LoginWithEmailAddress(request,
    [](const LoginResult& result, void* customData) { /* your login-success lambda here */ },
    [](const PlayFabError& error, void* customData) { /* your error lambda here */ },
    nullptr);
0

Source: https://habr.com/ru/post/1651721/


All Articles