Skip to content

Latest commit

 

History

History
517 lines (438 loc) · 13 KB

218.md

File metadata and controls

517 lines (438 loc) · 13 KB
Info

Example

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

class app_interface {
 public:
  constexpr explicit(true) app_interface(const iapi& api) : api_{api} {}
  auto run() const -> int { return api_.call(); }

 private:
  const iapi& api_;
};

template<class TApi>
class app_template {
 public:
  constexpr explicit(true) app_template(const TApi& api) : api_{api} {}
  auto run() const -> int { return api_.call(); }

 private:
  const TApi& api_;
};

template<class T>
concept api = requires(T t) {
  t.call();
};

template<api TApi>
class app_concept {
 public:
  constexpr explicit(true) app_concept(const TApi& api) : api_{api} {}
  auto run() const -> int { return api_.call(); }

 private:
  const TApi& api_;
};

int main() {
  // interface injection
  {
    struct : iapi {
      auto call() const -> int override { return 42; }
    } fake_api;
    app_interface app{fake_api};
    assert(42 == app.run());
  }

  // template injection
  {
    iapi api{};
    app_template app{api};
    assert(42 == app.run());
  }

  // concept injection
  {
    iapi api{};
    app_concept app{api};
    assert(42 == app.run());
  }
}

https://godbolt.org/z/qM85TT186

Puzzle

  • Can you implement required steps with injecting api via {interface, template, concept}?

    • Double points for injection via type erasure!
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;                     // TODO
template<class TApi> class app_template; // TODO
template<api TApi>   class app_concept;  // TODO

// NOTE: Double points for injection via type erasure!

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) {
          // 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/YsGzxodMc

Solutions

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

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

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

class app_erasure {
    const std::any api_;
public:
    app_erasure(const auto& api) : api_{api} {}
    auto run() const {
        return std::any_cast<const iapi&>(api_).call();
    }
};

struct fake_api : iapi {
    fake_api(int value):value_{value}{}
    int value_{};
    virtual int call() const override {
        return value_;
    }
};

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

        steps.given("I have an app interface") = [&] {
          const app_interface app{fake_};

          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 app_template<fake_api> app{fake_};

          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 app_concept<fake_api> app{fake_};

          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 app erasure") = [&] {
          const app_erasure app{fake_};

          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+1 == result);
          };
        };

      };
    };
  };
};

https://godbolt.org/z/xhc3MfsbG

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;
};

bdd::gherkin::steps steps = [](auto& steps) {
  steps.feature("Dependency Injection*") = [&] {
    steps.scenario("*") = [&] {
      steps.given("I have an api which returns {}") = [&](int value) {
        struct fake_api:public iapi
        {
            fake_api(int v):v(v){}
            int call() const override{ return v; }
            int v;
        };
        fake_api my_api(value);

        steps.given("I have an app interface") = [&] {
          app_interface app{my_api};

          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{my_api};
          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{my_api};

          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);
          };
        };

      };
    };
  };
};

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

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

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

class app_concept {
public:
  template <api TApi>
  app_concept(const TApi& a) : api_{std::make_unique<erased_api<TApi>>(a)} {}
  const auto run() const { return api_->call(); }

private:
  template <api TApi>
  struct erased_api : iapi {
    erased_api(const TApi& a) : api_{a} {}
    auto call() const -> int override { return api_.call(); }
    TApi api_;
  };

  std::unique_ptr<iapi> api_;
};

bdd::gherkin::steps steps = [](auto& steps) {
  steps.feature("Dependency Injection*") = [&] {
    steps.scenario("*") = [&] {
      steps.given("I have an api which returns {}") = [&](int value) {
        struct local_api : iapi {
          local_api(int value) : value_{value} {}
          auto call() const -> int override { return value_; }
          int value_;
        } a{value};

        steps.given("I have an app interface") = [&] {
          const auto app = app_interface{a};
          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 = app_template{a};
          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 = app_concept{a};
          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);
          };
        };
      };
    };
  };
};

https://godbolt.org/z/6bnjW6M91

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

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

template<api TApi>   class app_concept {
public:
    constexpr explicit(true) app_concept(const TApi& api) : api_{api} {}
    auto run() const -> int { return api_.call(); }
private:
    const TApi& api_;
};

bdd::gherkin::steps steps = [](auto& steps) {
  steps.feature("Dependency Injection*") = [&] {
    steps.scenario("*") = [&] {
      steps.given("I have an api which returns {}") = [&](int value) {
         api1 myApi(value);
        steps.given("I have an app interface") = [&] {
          app_interface app(myApi);
          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(myApi);
          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(myApi);
          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);
          };
        };

      };
    };
  };
};

https://godbolt.org/z/cxP4dc8EE