I'm wrapping a C function in a C++ callable. The C function accepts a function pointer (with state). A code sample says a thousand words so...
template<typename IteratorFunctor>
MatchCollection match(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)
};
detail::setmatch_TokenIterator cFunctionPtr = [](void* pContext) -> detail::setmatch_String
{
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 detail::setmatch_String{nullptr, 0};
else
return detail::setmatch_String{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
detail::setmatch_MatchCollection temp;
const char* err = detail::setmatch_match(db, cFunctionPtr, &context, &temp);
if(err) throw std::runtime_error(err);
return MatchCollection(temp.matchedIds, temp.count);
}
So this supports functors that return either references or value types, however it does not support functors that return pointers. I'd love to also allow the functor to return a pointer to something.
Is there a good way to do this using C++11 features (and no dependencies or weird compiler hacks, since this is part of the public API)?
Aucun commentaire:
Enregistrer un commentaire