Функция шаблона, которая поддерживает ссылки, значения и указатели? - PullRequest
0 голосов
/ 27 января 2019

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

1 Ответ

0 голосов
/ 28 января 2019
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)() );
    };
  }
};

это оборачивает лямбду или другой 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...)).

Сшейте их вместе, и это должно работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...