Skip to content

Commit

Permalink
support container::invoke()
Browse files Browse the repository at this point in the history
  • Loading branch information
romanpauk committed Jul 28, 2024
1 parent 67d357c commit 867849d
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 15 deletions.
22 changes: 12 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(CMAKE_CXX_STANDARD 17 CACHE STRING "c++ standard")
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if (NOT CMAKE_CXX_STANDARD MATCHES "17|20|23")
message(FATAL_ERROR "unsupported c++ standard version: ${CMAKE_CXX_STANDARD}")
Expand All @@ -66,7 +67,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND)

option(DINGO_TESTING_ENABLED "enable testing through googletest" ON)
Expand All @@ -79,10 +80,10 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
# TODO: g++ -Wall -Wextra -Wpedantic -Q --help=warning | grep disabled
set(DINGO_WARNINGS_GCC
-Wall
-Wpedantic
-Wextra
-Wpedantic
-Wextra
-Wshadow
-Wno-unused-variable
-Wno-unused-variable
-Wno-gnu-zero-variadic-macro-arguments
)

Expand All @@ -107,8 +108,8 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
if(DINGO_BENCHMARK_ENABLED)
FetchContent_Declare(googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.8.3)
GIT_TAG v1.8.3)

set(BENCHMARK_ENABLE_TESTING off)
FetchContent_MakeAvailable(googlebenchmark)

Expand All @@ -121,7 +122,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
FetchContent_MakeAvailable(googlefruit)
endif()
endif()

if(DINGO_TESTING_ENABLED)
add_executable(dingo_test
test/allocator.cpp
Expand All @@ -134,6 +135,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
test/dingo.cpp
test/external.cpp
test/index.cpp
test/invoke.cpp
test/multibindings.cpp
test/nesting.cpp
test/shared.cpp
Expand All @@ -144,7 +146,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
)
add_test(NAME dingo_test COMMAND dingo_test)
target_link_libraries(dingo_test dingo gtest_main)

target_compile_options(dingo_test PRIVATE $<$<CXX_COMPILER_ID:GNU,Clang>:${DINGO_WARNINGS_GCC}>)
target_compile_options(dingo_test PRIVATE $<$<CXX_COMPILER_ID:MSVC>:${DINGO_WARNINGS_MSVC} /bigobj>)

Expand All @@ -158,7 +160,7 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
benchmark/index.cpp
)

target_link_libraries(dingo_benchmark
target_link_libraries(dingo_benchmark
dingo
benchmark::benchmark
)
Expand All @@ -183,4 +185,4 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
group_sources(test)
group_sources(benchmark)
endif()
endif()
endif()
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ struct D {

// Construct an un-managed struct using dependencies from the container
D d = container.construct<D>();

// Invoke callable
D e = container.invoke([&](A& a, B* b) { return D{a, b}; });
```
<!-- } -->
Expand Down Expand Up @@ -552,8 +555,8 @@ disambiguating the registration with an user-provided tag. See
#### Constructing Unmanaged Types
It is possible to let a container construct an unknown type using registered
dependencies to construct it.
Unregistered types can be constructed using registered dependencies with
construct() member function.
<!-- { include("examples/construct.cpp", scope="////") -->
Expand All @@ -577,6 +580,30 @@ B b = container.construct<B>();

<!-- } -->

#### Invoking Callables

Callable objects can be called using invoke() member function with arguments
provided by the container. Supported callable types are lambdas, std::function
and function pointers.

<!-- { include("examples/invoke.cpp", scope="////") -->

Example code included from [examples/invoke.cpp](examples/invoke.cpp):

```c++
// struct B that will be constructed using container
struct B {
A& a;
static B factory(A& a) { return B{a}; }
};
// Construct instance of B, injecting shared instance of A
B b1 = container.invoke([&](A& a) { return B{a}; });
B b2 = container.invoke(std::function<B(A&)>([](auto& a) { return B{a}; }));
B b3 = container.invoke(B::factory);
```
<!-- } -->
#### Customizable RTTI
For non-RTTI enabled builds, it is possible to parametrize the container with
Expand Down
3 changes: 2 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_example(factory_constructor.cpp)
add_example(factory_constructor_concrete.cpp)
add_example(factory_static_function.cpp)
add_example(index.cpp)
add_example(invoke.cpp)
add_example(message_processing.cpp)
add_example(multibindings.cpp)
add_example(nesting.cpp)
Expand All @@ -22,4 +23,4 @@ add_example(scope_external.cpp)
add_example(scope_shared.cpp)
add_example(scope_shared_cyclical.cpp)
add_example(scope_unique.cpp)
add_example(service_locator.cpp)
add_example(service_locator.cpp)
33 changes: 33 additions & 0 deletions examples/invoke.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// This file is part of dingo project <https://github.com/romanpauk/dingo>
//
// See LICENSE for license and copyright information
// SPDX-License-Identifier: MIT
//

#include <dingo/container.h>
#include <dingo/storage/shared.h>

// struct A that will be registered with the container
struct A {};

////
// struct B that will be constructed using container
struct B {
A& a;
static B factory(A& a) { return B{a}; }
};
////
int main() {
using namespace dingo;
container<> container;
// Register struct A with shared scope
container.register_type<scope<shared>, storage<A>>();
////
// Construct instance of B, injecting shared instance of A
B b1 = container.invoke([&](A& a) { return B{a}; });
B b2 = container.invoke(std::function<B(A&)>([](auto& a) { return B{a}; }));
B b3 = container.invoke(B::factory);
////
}

3 changes: 3 additions & 0 deletions examples/quick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,8 @@ int main() {

// Construct an un-managed struct using dependencies from the container
D d = container.construct<D>();

// Invoke callable
D e = container.invoke([&](A& a, B* b) { return D{a, b}; });
////
}
11 changes: 9 additions & 2 deletions include/dingo/container.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <dingo/decay.h>
#include <dingo/exceptions.h>
#include <dingo/factory/callable.h>
#include <dingo/factory/invoke.h>
#include <dingo/index.h>
#include <dingo/resolving_context.h>
#include <dingo/rtti/static_type_info.h>
Expand Down Expand Up @@ -250,6 +251,12 @@ class container : public allocator_base<Allocator> {
return results;
}

template< typename Callable > auto invoke(Callable&& callable) {
resolving_context context;
return ::dingo::invoke< std::remove_reference_t<Callable> >::construct(
context, *this, std::forward<Callable>(callable));
}

private:
template <typename... TypeArgs, typename Arg, typename IdType>
auto& register_type_impl(Arg&& arg, IdType&& id) {
Expand All @@ -265,7 +272,7 @@ class container : public allocator_base<Allocator> {
// don't need to be created and because of that, the stored element
// could be referenced in most usages as it will no longer be a
// temporary object. The code below does the rewrite of storage type
// into stored type.
// into stored type.
//
using interface_type_0 = std::tuple_element_t<
0, typename registration::interface_type::type_tuple>;
Expand Down Expand Up @@ -546,4 +553,4 @@ class container : public allocator_base<Allocator> {
typename ContainerTraits::template type_cache_type<void*, allocator_type>
type_cache_;
};
} // namespace dingo
} // namespace dingo
44 changes: 44 additions & 0 deletions include/dingo/factory/invoke.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// This file is part of dingo project <https://github.com/romanpauk/dingo>
//
// See LICENSE for license and copyright information
// SPDX-License-Identifier: MIT
//

#pragma once

#include <dingo/config.h>
#include <dingo/factory/constructor.h>

#include <functional>

namespace dingo {
template< typename T > struct invoke: invoke< decltype(&T::operator()) > {};

template< typename T, typename R, typename... Args > struct invoke< R(T::*)(Args...) >: invoke< R(T::*)(Args...) const > {};

template< typename T, typename R, typename... Args > struct invoke< R(T::*)(Args...) const > {
template< typename Context, typename Container, typename Callable > static R construct(Context& ctx, Container& container, Callable&& callable) {
return callable(((void)sizeof(Args), detail::constructor_argument_impl< void, Context, Container, detail::reference >(ctx, container))...);
}
};

template< typename R, typename... Args > struct invoke< std::function<R(Args...)> > {
template< typename Context, typename Container, typename Callable > static R construct(Context& ctx, Container& container, Callable&& callable) {
return callable(((void)sizeof(Args), detail::constructor_argument_impl< void, Context, Container , detail::reference >(ctx, container))...);
}
};

template< typename R, typename... Args > struct invoke< R(*)(Args...) > {
template< typename Context, typename Container, typename Callable > static R construct(Context& ctx, Container& container, Callable&& callable) {
return callable(((void)sizeof(Args), detail::constructor_argument_impl< void, Context, Container, detail::reference >(ctx, container))...);
}
};

template< typename R, typename... Args > struct invoke< R(Args...) > {
template< typename Context, typename Container, typename Callable > static R construct(Context& ctx, Container& container, Callable&& callable) {
return callable(((void)sizeof(Args), detail::constructor_argument_impl< void, Context, Container, detail::reference >(ctx, container))...);
}
};

}
73 changes: 73 additions & 0 deletions test/invoke.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// This file is part of dingo project <https://github.com/romanpauk/dingo>
//
// See LICENSE for license and copyright information
// SPDX-License-Identifier: MIT
//

#include <dingo/container.h>
#include <dingo/storage/external.h>
#include <dingo/storage/shared.h>
#include <dingo/storage/unique.h>

#include <gtest/gtest.h>

#include "containers.h"
#include "test.h"

namespace dingo {

template <typename T> struct invoke_test : public test<T> {};
TYPED_TEST_SUITE(invoke_test, container_types);

template< typename T = void > struct invoke_test_functions {
static void call(int arg) { result = arg * 2; }
static int result;

static int call_return(int arg) { return arg * 2; }
};

template< typename T > int invoke_test_functions<T>::result;

TYPED_TEST(invoke_test, invoke) {
using container_type = TypeParam;

container_type container;
container.template register_type<scope<external>, storage<int>>(2);

{
std::function<int(int)> fn = [](int arg) { return arg * 2; };
ASSERT_EQ(container.invoke(fn), 4);
}

{
int result = 0;
std::function<void(int)> fn = [&](int arg) { result = arg * 2; };
container.invoke(fn);
ASSERT_EQ(result, 4);
}

{
ASSERT_EQ(container.invoke([](int arg) { return arg * 2; }), 4);
}

{
int result = 0;
container.invoke([&](int arg) { result = arg * 2; });
ASSERT_EQ(result, 4);
}

{
container.invoke(&invoke_test_functions<>::call);
ASSERT_EQ(invoke_test_functions<>::result, 4);
}

{
ASSERT_EQ(container.invoke(invoke_test_functions<>::call_return), 4);
ASSERT_EQ(container.invoke(&invoke_test_functions<>::call_return), 4);
}
}

}


0 comments on commit 867849d

Please sign in to comment.