Skip to content

Latest commit

 

History

History
640 lines (538 loc) · 16.9 KB

219.md

File metadata and controls

640 lines (538 loc) · 16.9 KB
Info

Example

class iapi {
 public:
  virtual ~iapi() = default;
  virtual auto call() const -> int = 0;
};

template<class T>
concept api = requires(const T& t) {
  { t.call() } -> std::same_as<int>;
};

template<class T1 = class iapi, // NOTE: class iapi for template injection
           api T2 = class iapi> // NOTE: class iapi for concepts injection
struct app {
  constexpr app(const T1& t1, const T2& t2, const iapi& t3) { // NOTE: iapi for interface injection
    assert(42 == t1.call() and 42 == t2.call() and 42 == t3.call());
  }
};

int main() {
  struct fake_api : iapi {
    auto call() const -> int { return 42; }
  };

  const auto injector = boost::di::make_injector(
    boost::di::bind<iapi>.to<fake_api>() // bind iapi to fake_api
  );

  boost::di::create<app>(injector); // return an app
}

https://godbolt.org/z/jEGK1z3nT

Puzzle

  • Can you implement required steps with Automatic Dependency Injection using DI library?
class iapi {
 public:
  virtual ~iapi() = default;
  virtual auto call() const -> int = 0;
};

template<class T>
concept api = requires(const T& t) {
  { t.call() } -> std::same_as<int>;
};

class app_interface;
template<class TApi /*= TODO*/> class app_template;
template<api TApi /*= TODO*/> class app_concept;

class fake_api : public iapi {
 public:
  constexpr explicit(true) fake_api(const int& value)
   : value{value}
  { }

  auto call() const -> int override { return value; }

 private:
  const int& value{};
};

int main() {
  using namespace boost::ut;

  bdd::gherkin::steps steps = [](auto& steps) {
    steps.feature("Dependency Injection*") = [&] {
      steps.scenario("*") = [&] {
        steps.given("I have an api which returns {}") = [&](int value) {

          const auto injector = boost::di::make_injector(
            // TODO
          );

          steps.given("I have an app interface") = [&] {
            // TODO

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app template") = [&] {
            // TODO

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app concept") = [&] {
            // TODO

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

        };
      };
    };
  };

  "app"_test = steps | R"(
      Feature: Dependency Injection
        Scenario: Via interface
          Given I have an api which returns 10
          Given I have an app interface
           When I call run on the app
           Then I should get 10 from app call
        Scenario: Via template
          Given I have an api which returns 100
          Given I have an app template
           When I call run on the app
           Then I should get 100 from app call
        Scenario: Via concept
          Given I have an api which returns 1000
          Given I have an app concept
           When I call run on the app
           Then I should get 1000 from app call
    )";
}

https://godbolt.org/z/5jqnaGsnE

Solutions

class iapi {
 public:
  virtual ~iapi() = default;
  virtual auto call() const -> int = 0;
};

template<class T>
concept api = requires(const T& t) {
  { t.call() } -> std::same_as<int>;
};

class app_interface{
    const iapi& api_;
public:
    app_interface(const iapi& api) : api_{api} {}
    auto run() const {
        return api_.call();
    }
};

template<class TApi = iapi> class app_template {
    const TApi& api_;
public:
    app_template(const TApi& api) : api_{api} {}
    auto run() const {
        return api_.call();
    }
};

template<api TApi = iapi> class app_concept {
    const TApi& api_;
public:
    app_concept(const TApi& api) : api_{api} {}
    auto run() const {
        return api_.call();
    }
};

class fake_api : public iapi {
 public:
  constexpr explicit(true) fake_api(const int& value)
   : value{value}
  { }

  auto call() const -> int override { return value; }

 private:
  const int& value{};
};

int main() {
  using namespace boost::ut;

  bdd::gherkin::steps steps = [](auto& steps) {
    steps.feature("Dependency Injection*") = [&] {
      steps.scenario("*") = [&] {
        steps.given("I have an api which returns {}") = [&](int value) {

          const auto injector = boost::di::make_injector(
            boost::di::bind<int>.to(value),
            boost::di::bind<iapi>.to<fake_api>()
          );

          steps.given("I have an app interface") = [&] {
            const auto& app = boost::di::create<app_interface>(injector);

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app template") = [&] {
            const auto& app = boost::di::create<app_template>(injector);

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app concept") = [&] {
            const auto& app = boost::di::create<app_concept>(injector);

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

        };
      };
    };
  };

  "app"_test = steps | R"(
      Feature: Dependency Injection
        Scenario: Via interface
          Given I have an api which returns 10
          Given I have an app interface
           When I call run on the app
           Then I should get 10 from app call
        Scenario: Via template
          Given I have an api which returns 100
          Given I have an app template
           When I call run on the app
           Then I should get 100 from app call
        Scenario: Via concept
          Given I have an api which returns 1000
          Given I have an app concept
           When I call run on the app
           Then I should get 1000 from app call
    )";
}

https://godbolt.org/z/r4K8cfaYh

class iapi {
 public:
  virtual ~iapi() = default;
  virtual auto call() const -> int = 0;
};

template<class T>
concept api = requires(const T& t) {
  { t.call() } -> std::same_as<int>;
};

class app_interface {
public:
  constexpr explicit(true) app_interface(const iapi& a) : api_{a} {}
  const auto run() const { return api_.call(); }
private:
  const iapi& api_;
};

template <class TApi = iapi> class app_template {
public:
  constexpr explicit(true) app_template(const TApi& a) : api_{a} {}
  const auto run() const { return api_.call(); }
private:
  const TApi& api_;
};

template <api TApi = iapi> using app_concept = app_template<TApi>;

class fake_api : public iapi {
 public:
  constexpr explicit(true) fake_api(const int& value)
   : value{value}
  { }

  auto call() const -> int override { return value; }

 private:
  const int& value{};
};

int main() {
  using namespace boost::ut;

  bdd::gherkin::steps steps = [](auto& steps) {
    steps.feature("Dependency Injection*") = [&] {
      steps.scenario("*") = [&] {
        steps.given("I have an api which returns {}") = [&](int value) {
          const auto injector = boost::di::make_injector(
            boost::di::bind<iapi>.to<fake_api>(),
            boost::di::bind<int>.to(value)
          );

          steps.given("I have an app interface") = [&] {
            const auto app = boost::di::create<app_interface>(injector);
            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app template") = [&] {
            const auto app = boost::di::create<app_template>(injector);
            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app concept") = [&] {
            const auto app = boost::di::create<app_concept>(injector);
            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

        };
      };
    };
  };

  "app"_test = steps | R"(
      Feature: Dependency Injection
        Scenario: Via interface
          Given I have an api which returns 10
          Given I have an app interface
           When I call run on the app
           Then I should get 10 from app call
        Scenario: Via template
          Given I have an api which returns 100
          Given I have an app template
           When I call run on the app
           Then I should get 100 from app call
        Scenario: Via concept
          Given I have an api which returns 1000
          Given I have an app concept
           When I call run on the app
           Then I should get 1000 from app call
    )";
}

https://godbolt.org/z/c8YYsaMGj

class iapi {
 public:
  virtual ~iapi() = default;
  virtual auto call() const -> int = 0;
};

template<class T>
concept api = requires(const T& t) {
  { t.call() } -> std::same_as<int>;
};

class app_interface {
public:
    app_interface(iapi& iapi) : _iapi(iapi) {}
    int run() { return _iapi.call(); }
private:
    iapi& _iapi;
};

template<class TApi = class iapi> class app_template {
public:
    app_template(TApi& tapi) : _tapi(tapi) {}
    int run() { return _tapi.call(); }
private:
    TApi& _tapi;
};

template<api TApi = class iapi> class app_concept {
public:
    app_concept(TApi& tapi) : _tapi(tapi) {}
    int run() { return _tapi.call(); }
private:
    TApi& _tapi;
};

class fake_api : public iapi {
 public:
  constexpr explicit(true) fake_api(const int& value)
   : value{value}
  { }

  auto call() const -> int override { return value; }

 private:
  const int& value{};
};

int main() {
  using namespace boost::ut;

  bdd::gherkin::steps steps = [](auto& steps) {
    steps.feature("Dependency Injection*") = [&] {
      steps.scenario("*") = [&] {
        steps.given("I have an api which returns {}") = [&](int value) {

          const auto injector = boost::di::make_injector(
            boost::di::bind<int>.to(value),
            boost::di::bind<iapi>.to<fake_api>()
          );

          steps.given("I have an app interface") = [&] {
            app_interface app(boost::di::create<app_interface>(injector));
            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app template") = [&] {
            app_template app(boost::di::create<app_template>(injector));
            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app concept") = [&] {
            app_concept app(boost::di::create<app_concept>(injector));
            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

        };
      };
    };
  };

  "app"_test = steps | R"(
      Feature: Dependency Injection
        Scenario: Via interface
          Given I have an api which returns 10
          Given I have an app interface
           When I call run on the app
           Then I should get 10 from app call
        Scenario: Via template
          Given I have an api which returns 100
          Given I have an app template
           When I call run on the app
           Then I should get 100 from app call
        Scenario: Via concept
          Given I have an api which returns 1000
          Given I have an app concept
           When I call run on the app
           Then I should get 1000 from app call
    )";
}

https://godbolt.org/z/MMqGPq8Po

class iapi {
 public:
  virtual ~iapi() = default;
  virtual auto call() const -> int = 0;
};

template<class T>
concept api = requires(const T& t) {
  { t.call() } -> std::same_as<int>;
};

class app_interface
{
    public:
    app_interface( iapi const & p_api ):m_api(p_api){}
    auto run(){ return m_api.call();}
    iapi const & m_api;
};
template<class TApi> class app_template
{
    public:
    app_template(TApi const & p_api):m_api(p_api){}
    auto run(){ return m_api.call();}
    TApi const& m_api;
};
template<api TApi>   class app_concept
{
    public:
    app_concept(TApi const & p_api):m_api(p_api){}
    auto run(){ return m_api.call();}
    TApi const& m_api;
};

class fake_api : public iapi {
 public:
  constexpr explicit(true) fake_api(const int& value)
   : value{value}
  { }

  auto call() const -> int override { return value; }

 private:
  const int& value{};
};

int main() {
  using namespace boost::ut;

  bdd::gherkin::steps steps = [](auto& steps) {
    steps.feature("Dependency Injection*") = [&] {
      steps.scenario("*") = [&] {
        steps.given("I have an api which returns {}") = [&](int value) {

          const auto injector = boost::di::make_injector(
            boost::di::bind<iapi>.to<fake_api>(),
            boost::di::bind<int>.to(value)
          );

          steps.given("I have an app interface") = [&] {
            auto app = boost::di::create<app_interface>(injector);

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app template") = [&] {
            auto app = boost::di::create<app_template<fake_api>>(injector);

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

          steps.given("I have an app concept") = [&] {
            auto app = boost::di::create<app_concept<fake_api>>(injector);

            auto run_result = 0;
            steps.when("I call run on the app") = [&] {
              run_result = app.run();
            };
            steps.then("I should get {} from app call") = [&](_i result) {
              expect(run_result == result);
            };
          };

        };
      };
    };
  };

  "app"_test = steps | R"(
      Feature: Dependency Injection
        Scenario: Via interface
          Given I have an api which returns 10
          Given I have an app interface
           When I call run on the app
           Then I should get 10 from app call
        Scenario: Via template
          Given I have an api which returns 100
          Given I have an app template
           When I call run on the app
           Then I should get 100 from app call
        Scenario: Via concept
          Given I have an api which returns 1000
          Given I have an app concept
           When I call run on the app
           Then I should get 1000 from app call
    )";
}

https://godbolt.org/z/4GjhTGrEa