Я оборачиваю функцию 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)?