blob: eb64791bf06012d44ae2918dac9a02d5757b5333 [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FRUIT_COMPONENT_STORAGE_ENTRY_H
#define FRUIT_COMPONENT_STORAGE_ENTRY_H
#include <fruit/impl/component_storage/binding_deps.h>
#include <fruit/impl/data_structures/arena_allocator.h>
#include <fruit/impl/data_structures/semistatic_graph.h>
#include <fruit/impl/fruit_internal_forward_decls.h>
namespace fruit {
namespace impl {
/**
* This represents a generic entry in ComponentStorage.
* We use a single POD (this struct) to represent any binding so that ComponentStorage can hold a single vector, instead
* of having to hold multiple vectors (each of which potentially requiring allocation/deallocation when a
* ComponentStorage is constructed/destroyed).
* This way each ComponentStorage can hold a single vector and do a single allocation.
*/
struct ComponentStorageEntry {
enum class Kind {
#if FRUIT_EXTRA_DEBUG
INVALID,
#endif
BINDING_FOR_CONSTRUCTED_OBJECT,
BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION,
BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION,
BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION,
COMPRESSED_BINDING,
MULTIBINDING_FOR_CONSTRUCTED_OBJECT,
MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION,
MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION,
// This is not an actual binding, it's an "addendum" to multibinding bindings that specifies how the multibinding
// vector can be created. Unlike real multibinding entries, this *can* be deduped.
MULTIBINDING_VECTOR_CREATOR,
LAZY_COMPONENT_WITH_NO_ARGS,
LAZY_COMPONENT_WITH_ARGS,
// Component replacements are stored as a REPLACEMENT_LAZY_COMPONENT_* entry followed by a REPLACED_LAZY_COMPONENT_*
// entry. Note that the args are independent: e.g. a component with args can be replaced by a component with no
// args. This also means that the type_id of the two entries can be different (since it's the type_id of the
// function signature rather than just of the Component<...>).
REPLACED_LAZY_COMPONENT_WITH_NO_ARGS,
REPLACED_LAZY_COMPONENT_WITH_ARGS,
REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS,
REPLACEMENT_LAZY_COMPONENT_WITH_ARGS,
// These markers are used in expandLazyComponents(), see the comments there for details.
COMPONENT_WITH_ARGS_END_MARKER,
COMPONENT_WITHOUT_ARGS_END_MARKER,
};
#if FRUIT_EXTRA_DEBUG
mutable
#endif
Kind kind;
// This is usually the TypeId for the bound type, except:
// * when kind==COMPRESSED_BINDING, this is the interface's TypeId
// * when kind==*LAZY_COMPONENT_*, this is the TypeId of the
// Component<...>-returning function.
TypeId type_id;
/**
* This represents an entry in ComponentStorage for a binding (not a multibinding) that holds an already-constructed
* object.
*/
struct BindingForConstructedObject {
using object_ptr_t = const void*;
// The already-constructed object. We do *not* own this, this object must outlive the injector.
// This is a const pointer because in some cases it might be a const binding.
// We can cast this to a non-const pointer when we're sure that the original binding was for a non-const reference.
object_ptr_t object_ptr;
#if FRUIT_EXTRA_DEBUG
bool is_nonconst;
#endif
};
/**
* This represents an entry in ComponentStorage for a binding (not a multibinding) that holds an object that needs to
* be constructed (potentially after injecting any dependencies).
*/
struct BindingForObjectToConstruct {
// This is a const pointer because this might be a const binding. If not, we'll cast this back to a non-const
// pointer when we need to.
using object_t = const void*;
using create_t = object_t (*)(InjectorStorage&, SemistaticGraph<TypeId, NormalizedBinding>::node_iterator);
// The return value of this function is a pointer to the constructed object (guaranteed to be !=nullptr).
// Once the object is constructed (at injection time), the injector owns that object.
create_t create;
// The type IDs that this type depends on.
const BindingDeps* deps;
#if FRUIT_EXTRA_DEBUG
bool is_nonconst;
#endif
};
/**
* This represents an entry in ComponentStorage for a multibinding that holds an already-constructed
* object.
*/
struct MultibindingForConstructedObject {
using object_ptr_t = void*;
// The already-constructed object. We do *not* own this, this object must outlive the injector.
object_ptr_t object_ptr;
};
/**
* This represents an entry in ComponentStorage for a multibinding that holds an object that needs to
* be constructed (potentially after injecting any dependencies).
*/
struct MultibindingForObjectToConstruct {
using object_t = void*;
using create_t = object_t (*)(InjectorStorage&);
// The return value of this function is a pointer to the constructed object (guaranteed to be !=nullptr).
// Once the object is constructed (at injection time), the injector owns that object.
create_t create;
// The type IDs that this type depends on.
const BindingDeps* deps;
};
/**
* This is not an actual binding, it's an "addendum" to multibinding bindings that specifies how the multibinding
* vector can be created. Unlike real multibinding entries, this *can* be deduped.
*/
struct MultibindingVectorCreator {
using get_multibindings_vector_t = std::shared_ptr<char> (*)(InjectorStorage&);
// Returns the std::vector<T*> of instances, or nullptr if none.
// Caches the result in the `v' member of NormalizedMultibindingData.
get_multibindings_vector_t get_multibindings_vector;
};
// A CompressedBinding with interface_id==getTypeId<I>() and class_id==getTypeId<C>() means that if:
// * C is not exposed by the component
// * I is the only node that depends on C
// * There are no multibindings that directly depend on C
// The BindingData for C is BindingForObjectToConstruct(
// Then, taken create1, needs_reallocation such that the ComponentStorageEntry for c_type_id is
// BindingForObjectToConstruct(createC, deps, needs_allocation), we can remove the binding for I and C and replace
// them
// with just a binding for I, with BindingForObjectToConstruct(create, deps, needs_allocation).
struct CompressedBinding {
using create_t = BindingForObjectToConstruct::create_t;
// TypeId for the implementation.
TypeId c_type_id;
// The return value of this function is a pointer to the constructed object (guaranteed to be !=nullptr).
// Once the object is constructed (at injection time), the injector owns that object.
create_t create;
};
/**
* This represents an entry in ComponentStorage for a lazy component with no arguments.
*/
struct LazyComponentWithNoArgs {
// An arbitrary function type, used as type for the field `erased_fun`.
// Note that we can't use void* here, since data pointers might not have the same size as function pointers.
using erased_fun_t = void (*)();
// The function that will be invoked to create the Component.
// Here we don't know the type, it's only known at construction time.
erased_fun_t erased_fun;
using entry_vector_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
// The function that allows to add this component's bindings to the given ComponentStorage.
using add_bindings_fun_t = void (*)(erased_fun_t, entry_vector_t&);
add_bindings_fun_t add_bindings_fun;
template <typename Component>
static void addBindings(erased_fun_t erased_fun, entry_vector_t& entries);
template <typename Component>
static ComponentStorageEntry create(Component (*fun)());
template <typename Component>
static ComponentStorageEntry create(fruit::ComponentFunction<Component> component_function);
template <typename Component>
static ComponentStorageEntry createReplacedComponentEntry(Component (*fun)());
template <typename Component>
static ComponentStorageEntry createReplacementComponentEntry(Component (*fun)());
bool operator==(const LazyComponentWithNoArgs&) const;
void addBindings(entry_vector_t& entries) const;
std::size_t hashCode() const;
bool isValid() const;
};
/**
* This represents an entry in ComponentStorage for a lazy component with arguments.
*/
struct LazyComponentWithArgs {
class ComponentInterface {
public:
// An arbitrary function type, used as type for the field `erased_fun`.
// Note that we can't use void* here, since data pointers might not have the same size as function pointers.
using erased_fun_t = void (*)();
// The function that will be invoked to create the Component.
// Here we don't know the type, it's only known to the LazyComponent implementation.
// We store this here instead of in the LazyComponent implementation so that we can do a quick comparison on the
// pointer without virtual calls (and we can then do the rest of the comparison via virtual call if needed).
erased_fun_t erased_fun;
using entry_vector_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
explicit ComponentInterface(erased_fun_t erased_fun);
virtual ~ComponentInterface() = default;
// Checks if *this and other are equal, assuming that this->fun and other.fun are equal.
virtual bool areParamsEqual(const ComponentInterface& other) const = 0;
bool operator==(const ComponentInterface& other) const;
virtual void addBindings(entry_vector_t& component_storage_entries) const = 0;
virtual std::size_t hashCode() const = 0;
virtual ComponentInterface* copy() const = 0;
/**
* Returns the type ID of the real `fun` object stored by the implementation.
* We use this instead of the `typeid` operator so that we don't require RTTI.
*/
virtual TypeId getFunTypeId() const = 0;
};
template <typename Component, typename... Args>
static ComponentStorageEntry create(Component (*fun)(Args...), std::tuple<Args...> args_tuple);
template <typename Component, typename Arg, typename... Args>
static ComponentStorageEntry create(fruit::ComponentFunction<Component, Arg, Args...> component_function);
template <typename Component, typename... Args>
static ComponentStorageEntry createReplacedComponentEntry(Component (*fun)(Args...),
std::tuple<Args...> args_tuple);
template <typename Component, typename... Args>
static ComponentStorageEntry createReplacementComponentEntry(Component (*fun)(Args...),
std::tuple<Args...> args_tuple);
LazyComponentWithArgs(LazyComponentWithArgs&&) = default;
LazyComponentWithArgs& operator=(LazyComponentWithArgs&&) = default;
// Note: we must allow these (and use the default implementations) since this class is used in a union so it must be
// a POD. However when we need a real object we must call the other constructor above, and when we need a copy we
// must
// call copy() explicitly.
LazyComponentWithArgs() = default; // LCOV_EXCL_LINE
LazyComponentWithArgs(const LazyComponentWithArgs&) = default;
LazyComponentWithArgs& operator=(const LazyComponentWithArgs&) = default;
LazyComponentWithArgs copy() const;
void destroy() const;
ComponentInterface* component;
};
union {
// Valid iff kind is BINDING_FOR_CONSTRUCTED_OBJECT.
BindingForConstructedObject binding_for_constructed_object;
// Valid iff kind is BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_[NO_]ALLOCATION
// or BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION.
BindingForObjectToConstruct binding_for_object_to_construct;
// Valid iff kind is MULTIBINDING_FOR_CONSTRUCTED_OBJECT.
MultibindingForConstructedObject multibinding_for_constructed_object;
// Valid iff kind is MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_[NO_]ALLOCATION.
MultibindingForObjectToConstruct multibinding_for_object_to_construct;
// Valid iff kind is MULTIBINDING_VECTOR_CREATOR.
MultibindingVectorCreator multibinding_vector_creator;
// Valid iff kind is COMPRESSED_BINDING.
CompressedBinding compressed_binding;
// Valid iff kind is LAZY_COMPONENT_WITH_NO_ARGS, REPLACED_LAZY_COMPONENT_WITH_NO_ARGS or
// REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS.
LazyComponentWithNoArgs lazy_component_with_no_args;
// Valid iff kind is LAZY_COMPONENT_WITH_ARGS, REPLACED_LAZY_COMPONENT_WITH_ARGS or
// REPLACEMENT_LAZY_COMPONENT_WITH_ARGS.
LazyComponentWithArgs lazy_component_with_args;
};
// We use a custom method instead of a real copy constructor so that all copies are explicit (since copying is a
// fairly expensive operation).
ComponentStorageEntry copy() const;
// We use a custom method instead of a real destructor, so that we can hold these in a std::vector but still destroy
// them when desired.
void destroy() const;
};
// We can't have this assert in debug mode because we add debug-only fields that increase the size.
#if !FRUIT_EXTRA_DEBUG
// This is not required for correctness, but 4 64-bit words should be enough to hold this object, if not we'd end up
// using more memory/CPU than expected.
static_assert(sizeof(ComponentStorageEntry) <= 4 * sizeof(std::uint64_t),
"Error: a ComponentStorageEntry doesn't fit in 32 bytes as we expected");
#endif
} // namespace impl
} // namespace fruit
#include <fruit/impl/component_storage/component_storage_entry.defn.h>
#endif // FRUIT_COMPONENT_STORAGE_ENTRY_H