支持引用,值和指针的模板函数?

时间:2019-01-27 20:33:48

标签: c++ c++11 templates pointers reference

我将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::stringstd::string&。它还允许用户返回自己的类型,只要该类型具有length()data()方法即可。不过,它不允许函子返回std::string*,这是我要支持的功能。

是否有使用C ++ 11功能(并且没有依赖关系或奇怪的编译器黑客,因为这是公共API的一部分)来实现此目的的好方法?

1 个答案:

答案 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...))

将它们缝合在一起,应该可以工作。