簡體   English   中英

我可以從C調用std :: function嗎?

[英]Can I call a std::function from C?

我有一些返回std::function C ++代碼。 我想從一些C代碼中調用它。 這可能嗎? 例如,我有以下代碼:

typedef std::function<int(int)> AdderFunction;

AdderFunction makeAdder(int amount) {
    return [amount] (int n) {
        return n + amount;
    };
}

extern "C" {
    AdderFunction makeCAdder(int amount) {
        return makeAdder(amount);
    }
}

使用clang++ -std=c++11 test.cpp會導致以下警告:

'makeCAdder' has C-linkage specified, but returns user-defined type 'AdderFunction' (aka 'function<int (int)>') which is incompatible with C

我了解為什么會這樣,但是想知道是否有某種模式可以實現?

在C / C ++之間進行接口連接的最可移植的方法是使用指針在語言之間傳遞數據,並使用非成員函數進行函數調用。

.h文件:

#ifdef __cplusplus
extern "C" {
#endif

   // Declare the struct.
   struct Adder;

   // Declare functions to work with the struct.
   Adder* makeAdder(int amount);

   int invokeAdder(Adder* adder, int n);

   void deleteAdder(Adder* adder);

#ifdef __cplusplus
}
#endif

在.cpp文件中以以下方式實現它們:

#include <functional>

typedef std::function<int(int)> AdderFunction;
struct Adder
{
   AdderFunction f;
};

AdderFunction makeAdderFunction(int amount) {
    return [amount] (int n) {
        return n + amount;
    };
}

Adder* makeAdder(int amount)
{
   Adder* adder = new Adder;
   adder->f = makeAdderFunction(amount);
   return adder;
}

int invokeAdder(Adder* adder, int n)
{
   return adder->f(n);
}

void deleteAdder(Adder* adder)
{
   delete adder;
}

從C調用std::function是不可能的,因為C不支持所需的語言功能。 C沒有模板,訪問修飾符,可調用對象,虛擬方法或std::function可能在幕后使用的任何其他內容。 您需要提出C可以理解的策略。

一種這樣的策略是將std::function復制/移動到堆中,並將其作為不透明指針返回。 然后,您將通過C ++接口提供另一個函數,該函數采用該不透明指針並調用其中包含的函數。

// C side
struct function_opaque;
int call_opaque(struct function_opaque*, int param);

// C++ side
extern "C" {
    struct function_opaque {
        std::function<int(int)> f;
    };

    int call_opaque(function_opaque* func, int param) {
        return func->f(param);
    }
};

當然,這帶有內存管理的含義。

您需要至少將typedef放在extern "C"塊中(以使其編譯為C ++)。 我不確定這是否可以在C中使用。 在C中工作的只是使用普通的函數指針,例如

extern "C" {
using AdderFunction = int(int);
// old-style: typedef int(*AdderFunction)(int);
}

編輯:如果您使用的API可以為您提供std::function對象,則可以使用std::function::target()方法獲取其引用的(C可調用)原始函數指針。

using AdderFunction = std::function<int(int)>;
extern "C" {
using CAdderFunction = int(int);
CAdderFunction makeCAdder(int amount)
{
        return makeAdder(amount).target<CAdderFunction>();
}
}

另一個解決方案是將std::function拆分為一個指向閉包的指針和一個指向成員函數的指針,並將三件事傳遞給要調用lambda的C函數:

  • 知道如何安全地在閉包上調用該函數的C ++函數的地址
  • 閉包指針(不安全地轉換為void *
  • 成員函數指針(隱藏在包裝結構中,並強制轉換為void *

這是一個示例實現。

#include <functional>
#include <iostream>

template<typename Closure, typename Result, typename... Args>
struct MemberFunctionPointer
{
    Result (Closure::*value)(Args...) const;
};

template<typename Closure, typename Result, typename... Args>
MemberFunctionPointer<Closure, Result, Args...>
member_function_pointer(
    Result (Closure::*const value)(Args...) const)
{
    return MemberFunctionPointer<Closure, Result, Args...>{value};
}

template<typename Closure, typename Result, typename... Args>
Result
call(
    const void *const function,
    const void *const closure,
    Args... args)
{
    return
        ((reinterpret_cast<const Closure *>(closure))
        ->*(reinterpret_cast<const MemberFunctionPointer<Closure, Result, Args...>*>(function)->value))
        (std::forward<Args>(args)...);
}

C端的用法示例:

int
c_call(
    int (*const caller)(const void *, const void *, int),
    const void *const function,
    const void *const closure,
    int argument)
{
    return caller (function, closure, argument);
}

C ++方面的用法示例:

int
main()
{
    int captured = 5;
    auto unwrapped = [captured] (const int argument) {
        return captured + argument;
    };
    std::function<int(int)> wrapped = unwrapped;

    auto function = member_function_pointer(&decltype(unwrapped)::operator());
    auto closure = wrapped.target<decltype(unwrapped)>();
    auto caller = &call<decltype(unwrapped), int, int>;
    std::cout
        << c_call(
            caller,
            reinterpret_cast<const void *>(&function),
            reinterpret_cast<const void *>(closure),
            10)
        << '\n';
}

包裝器結構的原因是您不能將成員函數指針強制轉換為void *或任何其他對象指針類型,即使使用reinterpret_cast也不行,因此,我們傳遞成員函數指針的地址 您可以選擇將MemberFunctionPointer結構放置在堆上,例如,使用unique_ptr ,如果該結構的生存期比此簡單示例中的生存期更長。

您還可以將這三個參數包裝在C端的單個結構中,而不是分別傳遞它們:

struct IntIntFunction
{
    int (*caller)(const void *, const void *, int);
    const void *function;
    const void *closure;
};

#define INVOKE(f, ...) ((f).caller((f).function, (f).closure, __VA_ARGS__))

int
c_call(IntIntFunction function)
{
    return INVOKE(function, 10);
}

此解決方案的問題是當您使用參數值調用makeAdder時。無法解決,但我發布的目的是為了防止其他人使用。

template <typename FunctionPointerType, typename Lambda, typename ReturnType, typename ...Args>
inline FunctionPointerType MakeFunc(Lambda&& lambda, ReturnType (*)(Args...))
{
    thread_local std::function<ReturnType(Args...)> func(lambda);

    struct Dummy
    {
        static ReturnType CallLambda(Args... args)
        {
            return func(std::forward<Args>(args)...);
        }
    };

    return &Dummy::CallLambda;
}

template <typename FunctionPointerType, typename Lambda>
FunctionPointerType MakeFunc(Lambda&& lambda)
{
    return MakeFunc<FunctionPointerType, Lambda>(std::forward<Lambda>(lambda), FunctionPointerType());
}


typedef int(*AdderFunction)(int);

AdderFunction makeAdder(int amount) {
    return MakeFunc<int(*)(int)>([amount] (int n) {
        return n + amount;
    });
}

extern "C" {
    typedef int(*CAdderFunction)(int);

    CAdderFunction makeCAdder(int amount)
    {
        return makeAdder(amount);
    }
}

它通過將lambda存儲在線程本地std::function 然后返回一個指向靜態函數的指針,該函數將使用傳入的參數調用lambda。

我考慮過使用unordered_map並跟蹤每個makeAdder調用,但是您不能從靜態上下文中引用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM