Внедрение зависимостей в библиотеку продолжения C

#c #templates #dependency-injection #c 17

#c #шаблоны #внедрение зависимостей #c 17

Вопрос:

Как мне внедрить зависимость в класс, который я не могу описать без шаблона.

Проблема: я пишу библиотеку, в которой я хотел бы использовать зависимость от библиотеки продолжения от хост-приложения, известного как пользователи библиотеки, вместо того, чтобы включать что-то вроде pplx как часть моей библиотеки.

 template<typename _ReturnType>
struct ITask
{
    template<typename _ThenReturnType>
    virtual std::shared_ptr<ITask<_ThenReturnType>> Then(const std::function<_ThenReturnType(_ReturnType)>amp;amp; func) = 0;
};

struct ITaskFactoryService
{
    template<typename _TaskReturnType>
    virtual std::shared_ptr<ITask<_TaskReturnType>> CreateTask(const std::function<_TaskReturnType()>) = 0;
};

struct IHostServices
{
    virtual std::shared_ptr<ITaskFactoryService> TaskFactoryService() noexcept = 0;
};
  

Я знаю, что это невозможно в C . то, что я ищу, — это дизайнерское решение моей более серьезной проблемы. Я надеялся, что хост-приложение (потребитель библиотеки) сможет реализовать это и передать его в мою библиотеку. Я делаю это, потому что у разных хостов могут быть разные библиотеки продолжения.

Комментарии:

1. Я мог бы передать указатели void, я думаю, но мне кажется, что это очень грязно:(

2. Под библиотекой вы подразумеваете некоторые заголовки / исходные файлы — или действительно что-то вроде dll?

3. Вы не можете использовать virtual с шаблонами…

4. Не могли бы вы добавить еще немного информации о ваших потребностях?

5. Я не думаю, что ответ изменится, если это только заголовок, скомпилированный из источника, если это общая или статическая библиотека. Я думаю, что приведенный мной пример можно использовать как псевдокод, чтобы понять, чего я пытаюсь достичь. И да, тот факт, что я не могу использовать virtual с шаблонами, именно поэтому я пытаюсь найти наилучший способ внедрения зависимостей, точные типы которых заранее неизвестны.

Ответ №1:

Насколько я вас понял, вы хотите иметь возможность заменить свою библиотеку продолжения другой. Для этого вы хотели бы написать свои собственные оболочки вокруг них.

В общем, я думаю, вам следует написать больше «кода на C «. ITask и так далее выглядят более «Java-подобными» кодами.

Обычно вы определяете свойства / гарантии, которые требуются для, скажем так, «Task-Like-Types».

Например, тип, подобный задаче, должен иметь функцию-член «Then», которая принимает тип, подобный задаче, и возвращает новый тип, подобный задаче.

Чтобы проверить, является ли тип «похожим на задачу», вы обычно используете свойства типа, которые проверяют ваши требования. Альтернативный, более простой подход заключается в использовании «Type-Tags».

Здесь вы можете найти некоторый код на C , который показывает, как решить вашу проблему с дизайном:

 #include <type_traits>
#include <iostream>

//type tags
namespace MyContinuationLib
{
    class TaskLikeTypeTag{};
    class NoType{};
}

//traits
namespace MyContinuationLib
{
    //can be used for SFINAE
    template<
      typename T
    >
    class IsTaskLikeType
    {  
    public:
        static constexpr bool value = std::is_base_of_v<TaskLikeTypeTag, T>;
    };
}

//task factory
namespace MyContinuationLib
{
    template<
        template<typename...> typename TaskImplTemplate
    >
    class TaskFactory
    {
    };

    class HostServices;

    template<
        typename T
        ,typename UsedHostServices = HostServices
        ,typename PureT = std::remove_reference_t<T>
        ,typename = std::enable_if_t<
            !MyContinuationLib::IsTaskLikeType<PureT>::value
        >
        ,typename Dummy = void
    >
    auto ForceTaskLikeType(Tamp;amp; callable)
    {
        using TaskFactory = typename UsedHostServices::TaskFactory;
        auto task = TaskFactory::CreateTask(callable);
        return task;
    }

    template<
        typename T
        ,typename PureT = std::remove_reference_t<T>
        ,typename = std::enable_if_t<
            MyContinuationLib::IsTaskLikeType<PureT>::value
        >
    >
    decltype(auto) ForceTaskLikeType(Tamp;amp; callable)
    {
        return callable;
    }
}


//some TaskImplementation (you would need to define it's properties / what it should do!)

namespace MyContinuationLib
{
    template<typename...>
    class MyTask{};

    template<
        typename Callable
    >
    class MyTask<Callable> : public TaskLikeTypeTag
    {
    private:
        Callable callable; //e.g. a lambda or a std::function
    public:
        constexpr MyTask(const Callableamp; c) : callable(c){}
    public:
        template<
            typename CustomReturnType = NoType
            ,typename Invokable
            , typename InvokeResult = std::invoke_result_t<Invokable>
            , typename ReturnType = std::conditional_t<
                std::is_same_v<CustomReturnType, NoType>,
                InvokeResult,
                CustomReturnType
            >
            ,typename Result = MyTask<ReturnType, Invokable>
        >
        Result Then(Invokableamp;amp; func)
        {
            return Result(
                [amp;]{
                    this->operator();
                    return static_cast<ReturnType>(func());
                    //what ever...
                }
            );
        }

        constexpr void SomeOtherTaskLikeFkt(){
            callable();
            std::cout << "SomeOtherTaskLikeFkt...";
        }

        constexpr decltype(auto) operator()(){
            return callable();
        }
    };

    template<>
    class TaskFactory<MyTask>
    {
    public:
        template<
            typename Callable
        >
        constexpr static decltype(auto) CreateTask(Callableamp;amp; callable)
        {
            using PureCallable = std::remove_reference_t<Callable>;
            return MyTask<PureCallable>{ callable };
        }
    };
}

//TaskFactories
namespace MyContinuationLib
{
    class HostServices{
    public:
        //here we define what impl should be used!
        using TaskFactory = MyContinuationLib::TaskFactory<MyTask>;
    };
}

int main(){
    using namespace MyContinuationLib;

    auto task = ForceTaskLikeType([]{
        std::cout << "Hello World!n";
    });

    task.SomeOtherTaskLikeFkt();

    return 0;
}
  

Если вы не понимаете код или его части, пожалуйста, спросите.