Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ If you are working from a checkout, the top-level CMake project also supports
- Non-intrusive registration of ordinary C++ types
- Explicit control over lifetime and stored representation
- Array support for raw arrays and smart-pointer-backed arrays
- Explicit variant construction and variant storage resolution
- Explicit variant construction plus whole-variant or held-alternative
resolution
- Constructor deduction with explicit factory overrides when needed
- Interface bindings and multibindings
- Indexed and annotated resolution
Expand Down
33 changes: 24 additions & 9 deletions docs/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,14 +287,22 @@ Dingo handles variants in two distinct places:
- `construct<std::variant<A, B>, constructor_detection<A>>()` constructs the
variant by selecting `A` as the alternative to build
- `register_type<..., storage<std::variant<A, B>>, factory<...>>()` lets the
container store and later resolve the whole variant value
container store the variant and later resolve either the whole variant or its
currently held alternative

The current rules are narrow on purpose:

- the selected alternative must appear exactly once in the variant type
- resolution is for the whole variant, not for `A` or `B` directly
- unique variant storage resolves as the whole variant value or rvalue
- shared and external variant storage resolve as references to the whole variant
- the whole variant remains resolvable
- uniquely occurring alternatives are also resolvable from variant storage
- if a requested alternative is published but the current instance holds a
different alternative, resolution fails as `type_not_convertible_exception`
- duplicate alternative types are rejected at compile time for direct resolution
- unique variant storage resolves the whole variant as a value or rvalue, and a
held alternative as a value or rvalue
- shared and external variant storage resolve the whole variant as values,
references, or pointers, and a held alternative as values, references, or
pointers

<!-- { include("../examples/container/variant.cpp", scope="////", summary="Variant construction and storage example") -->

Expand Down Expand Up @@ -324,22 +332,27 @@ construct_container.register_type<scope<external>, storage<float>>(3.5f);
construct_container
.construct<std::variant<A, B>, constructor_detection<A>>();
assert(std::holds_alternative<A>(detected));
assert(std::get<A>(detected).value == 7);

[[maybe_unused]] auto explicit_ctor =
construct_container.construct<std::variant<A, B>, constructor<B(float)>>();
assert(std::holds_alternative<B>(explicit_ctor));
assert(std::get<B>(explicit_ctor).value == 3.5f);

container<> unique_container;
unique_container.register_type<scope<unique>, storage<int>>();
unique_container.register_type<scope<unique>, storage<std::variant<A, B>>,
factory<constructor_detection<A>>>();

// Resolve the whole variant value from variant storage.
// Resolve either the whole variant or its currently held alternative.
[[maybe_unused]] auto value = unique_container.resolve<std::variant<A, B>>();
assert(std::holds_alternative<A>(value));
assert(std::get<A>(value).value == 0);

[[maybe_unused]] auto selected = unique_container.resolve<A>();

try {
unique_container.resolve<B>();
assert(false);
} catch (const type_not_convertible_exception&) {
}

std::variant<A, B> existing(std::in_place_type<A>, 9);
container<> external_container;
Expand All @@ -349,7 +362,9 @@ external_container.register_type<scope<external>, storage<std::variant<A, B>&>>(
[[maybe_unused]] auto& ref = external_container.resolve<std::variant<A, B>&>();
assert(&ref == &existing);
assert(std::holds_alternative<A>(ref));
assert(std::get<A>(ref).value == 9);

[[maybe_unused]] auto& held = external_container.resolve<A&>();
assert(&held == &std::get<A>(existing));
```

</details>
Expand Down
3 changes: 2 additions & 1 deletion docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ the feature you care about and start there.
- [examples/storage/array.cpp](../examples/storage/array.cpp): register and
resolve raw arrays plus smart-array forms
- [examples/container/variant.cpp](../examples/container/variant.cpp): construct
variants and store registrations whose storage is itself a variant
variants and resolve either the whole variant or its held alternative from
variant storage

## Interfaces, Collections, And Dispatch

Expand Down
17 changes: 12 additions & 5 deletions examples/container/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,30 @@ int main() {
construct_container.construct<std::variant<A, B>,
constructor_detection<A>>();
assert(std::holds_alternative<A>(detected));
assert(std::get<A>(detected).value == 7);

[[maybe_unused]] auto explicit_ctor =
construct_container.construct<std::variant<A, B>,
constructor<B(float)>>();
assert(std::holds_alternative<B>(explicit_ctor));
assert(std::get<B>(explicit_ctor).value == 3.5f);

container<> unique_container;
unique_container.register_type<scope<unique>, storage<int>>();
unique_container.register_type<
scope<unique>, storage<std::variant<A, B>>,
factory<constructor_detection<A>>>();

// Resolve the whole variant value from variant storage.
// Resolve either the whole variant or its currently held alternative.
[[maybe_unused]] auto value =
unique_container.resolve<std::variant<A, B>>();
assert(std::holds_alternative<A>(value));
assert(std::get<A>(value).value == 0);

[[maybe_unused]] auto selected = unique_container.resolve<A>();

try {
unique_container.resolve<B>();
assert(false);
} catch (const type_not_convertible_exception&) {
}

std::variant<A, B> existing(std::in_place_type<A>, 9);
container<> external_container;
Expand All @@ -67,6 +72,8 @@ int main() {
external_container.resolve<std::variant<A, B>&>();
assert(&ref == &existing);
assert(std::holds_alternative<A>(ref));
assert(std::get<A>(ref).value == 9);

[[maybe_unused]] auto& held = external_container.resolve<A&>();
assert(&held == &std::get<A>(existing));
////
}
7 changes: 6 additions & 1 deletion include/dingo/container.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,9 @@ class container : public allocator_base<Allocator> {
void check_interface_requirements() {
using normalized_type = normalized_type_t<Type>;
using normalized_interface_type = normalized_type_t<TypeInterface>;
constexpr bool is_alternative_type_interface =
is_alternative_type_interface_compatible_v<normalized_type,
normalized_interface_type>;

static_assert(!std::is_reference_v<TypeInterface>);
if constexpr (detail::is_array_like_type_v<Type>) {
Expand All @@ -546,7 +549,9 @@ class container : public allocator_base<Allocator> {
}
}
static_assert(
std::is_convertible_v<normalized_type*, normalized_interface_type*>);
std::is_convertible_v<normalized_type*, normalized_interface_type*> ||
is_alternative_type_interface,
"registered type must be pointer-convertible to the interface");
if constexpr (!std::is_same_v<normalized_type,
normalized_interface_type>) {
static_assert(detail::storage_interface_requirements_v<
Expand Down
4 changes: 2 additions & 2 deletions include/dingo/memory/arena_allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ template< typename Allocator = std::allocator<uint8_t> > class arena

template< typename T, std::size_t N > arena(T(&buffer)[N], std::size_t block_size = N * sizeof(T))
: arena(reinterpret_cast<uint8_t*>(buffer), N * sizeof(T), block_size) {
static_assert(std::is_trivial_v<T>);
static_assert(std::is_trivially_default_constructible_v<T> &&
std::is_trivially_copyable_v<T>);
}

template< typename T > arena(T& buffer, std::size_t block_size = sizeof(T))
Expand Down Expand Up @@ -238,4 +239,3 @@ bool operator != (const arena_allocator<T, Arena, AlignmentT>& x, const arena_al
}

}

24 changes: 24 additions & 0 deletions include/dingo/registration/type_registration.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,30 @@ struct deduced_interface_type {
using type = ::dingo::interfaces<leaf_type_t<StorageType>>;
};

template <typename StorageType, typename ScopeType>
struct deduced_interface_type<
StorageType, ScopeType,
std::void_t<typename ::dingo::detail::alternative_type_interface_types<
std::remove_cv_t<std::remove_reference_t<StorageType>>>::type>> {
using type = ::dingo::interfaces<
typename ::dingo::detail::alternative_type_interface_types<
std::remove_cv_t<std::remove_reference_t<StorageType>>>::type>;
};

template <typename StorageType, typename ScopeType>
struct deduced_interface_type<
StorageType, ScopeType,
std::enable_if_t<!std::is_same_v<typename ScopeType::type, unique> &&
type_traits<std::remove_cv_t<
std::remove_reference_t<StorageType>>>::enabled &&
type_traits<std::remove_cv_t<
std::remove_reference_t<StorageType>>>::is_value_borrowable &&
is_alternative_type_v<std::remove_cv_t<leaf_type_t<StorageType>>>>> {
using type = ::dingo::interfaces<
typename ::dingo::detail::alternative_type_interface_types<
std::remove_cv_t<leaf_type_t<StorageType>>>::type>;
};

template <typename StorageType, typename ScopeType>
struct deduced_interface_type<
StorageType, ScopeType,
Expand Down
7 changes: 7 additions & 0 deletions include/dingo/resolution/instance_resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,10 @@ struct instance_resolver<RTTI, Type, Storage, unique> {
return storage.resolve(context, container);
}

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4702)
#endif
template <typename Target, typename Source, typename Context,
typename Container, typename Factory>
void* resolve_address(Context& context, Container& container,
Expand All @@ -303,6 +307,9 @@ struct instance_resolver<RTTI, Type, Storage, unique> {
return ::dingo::get_address(
context, std::forward<decltype(instance)>(instance));
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif

private:
template <typename T, typename Context, typename... Args>
Expand Down
Loading