Info
-
Did you know about Automatic Dependency Injection libraries such as DI?
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
}
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
)";
}
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
)";
}
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
)";
}
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
)";
}
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
)";
}