代码之家  ›  专栏  ›  技术社区  ›  Robert Fraser

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

  •  1
  • Robert Fraser  · 技术社区  · 7 年前

    //======================================================
    // 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的一部分)有没有一个好的方法来做到这一点?

    1 回复  |  直到 5 年前
        1
  •  2
  •   Yakk - Adam Nevraumont    7 年前
    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++可调用的函数封装成函数指针和pVal.它没有别的作用。返回值被推导或传递。

    第二个问题是想要调整返回值。

    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

    最后写 compose(First, Second) 返回 Second(First(Args...)) .

    把这些缝在一起,它应该会起作用。