我将C函数包装在C ++函数中。 C函数接受函数指针(带有状态)。我想允许C ++可调用。一个代码示例说了一千个单词,所以...
//======================================================
// All this stuff is defined in C somewhere else
// C string type
typedef struct FooString { const char* str; size_t length; } FooString;
// C function pointer type
// This keeps getting called until it returns something with length == 0
typedef FooString (*CFunctionPointer)(void* state);
// Function being wrapped
void quux(CFunctionPointer fPtr, void* state)
{
FooString str;
while(1)
{
str = fPtr(state);
if(str.length == 0)
break;
else
{
// do something
}
}
}
//======================================================
// Here's what I need help with
template<typename IteratorFunctor>
void quuxWrapper(IteratorFunctor& iterator) const
{
// type that the functor returns, and related types
using TIn = decltype(iterator());
using TNoRef = typename std::remove_reference<TIn>::type;
using TYesRef = typename std::add_lvalue_reference<TNoRef>::type;
using TStored = typename std::conditional<std::is_reference<TIn>::value, std::reference_wrapper<TNoRef>, TIn>::type;
// store this on the stack in this function, and pass a pointer to it into the C library
// the C callback will pass back the pointer, and we can get at all this stuff from within the lambda
struct CallbackContext
{
bool isFirst; // is this the first iteration?
IteratorFunctor& iterator; // reference to the iterator in a place we can get to from inside the C function pointer callback
TStored current; // current value (either an actual value stored on the stack, or a reference wrapper)
};
CFunctionPointer cFunctionPtr = [](void* pContext) -> FooString
{
CallbackContext& context = *((CallbackContext*) pContext);
// on the first iteration, we return the value already fetched (need to do this to support things that
// aren't DefaultConstructable). On subsequent iterations, call the functor again.
if(context.isFirst)
context.isFirst = false;
else
context.current = context.iterator();
// this is needed for supporting both references as reference_wrappers and value types. we take a reference
// which forces reference_wrapper to call its conversion operator and is basically a no-op for value types
// (something like context.current.length would fail for reference_wrapper)
TYesRef current = context.current;
// stop iteration if functor returns anything with length 0
if(current.length() == 0)
return FooString{nullptr, 0};
else
return FooString{current.data(), current.length()};
};
// create the context and make the first call to the iterator
CallbackContext context{true, iterator, iterator()};
// and then call the C function
quux(cFunctionPtr, &context);
}
这支持从函子返回std::string
或std::string&
。它还允许用户返回自己的类型,只要该类型具有length()
和data()
方法即可。不过,它不允许函子返回std::string*
,这是我要支持的功能。
是否有使用C ++ 11功能(并且没有依赖关系或奇怪的编译器黑客,因为这是公共API的一部分)来实现此目的的好方法?
答案 0 :(得分:2)
template<class F, class R=std::result_of_t<F&()>>
struct c_callback {
F state;
void* get_pvoid() { return std::addressof(state); }
using C_sig = R(*)(void*);
static C_sig get_pfunc() {
return [](void* pvoid)->R {
F* pstate = static_cast<F*>(pvoid);
return static_cast<R>( (*state)() );
};
}
};
这将lambda或其他可调用的C ++包装到函数指针和pvoid中。它什么也没做。返回值可以推导或传递。
第二个问题是要调整返回值。
template<class T>
FooString to_foostring_f( T& t ) {
return {t.data(), t.length()};
}
template<class T>
FooString to_foostring_f( T* t ) {
if (!t) return {0,0};
return to_foostring_f(*t);
}
auto to_foostring = [](auto&& t)->FooString {
return to_foostring_f( decltype(t)(t) );
};
to_foostring
是一个函数对象,它接受某些内容并返回FooString
。它通过调用to_foostring_f
来实现。您可以使用ADL对其进行增强。
最后写出compose(First, Second)
并返回Second(First(Args...))
。
将它们缝合在一起,应该可以工作。