Last active
June 3, 2019 20:37
-
-
Save nonsequitur/a0965cff013c3e0bb780 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DiConfig { | |
public: | |
template <class InstanceType, class Deleter, class ...Deps> | |
void add(InstanceFactoryFunction<InstanceType, Deleter, Deps...> instance_factory); | |
Injector build_injector(); | |
private: | |
using InitializerFn = std::function<void(Injector&)>; | |
struct DependencyNode { | |
enum class Mark { Unmarked, Temp, Marked }; | |
Mark mark_ = Mark::Unmarked; | |
// Name of the service class, | |
// needed to display more useful error messages | |
std::string debug_type_name_; | |
// A function that invokes the instance factory | |
// and adds the created service to the given | |
// injector. | |
InitializerFn initializer_; | |
std::vector<int> dependencies_; | |
}; | |
void toposort_visit_node(int node_id, Injector& injector); | |
std::unordered_map<int, DependencyNode> graph_; | |
}; | |
template <class InstanceType, class Deleter, class ...Deps> | |
void DiConfig::add(InstanceFactoryFunction<InstanceType, Deleter, Deps...> instance_factory) | |
{ | |
int instance_type_id = detail::type_id<typename std::remove_const_t<InstanceType>>(); | |
DependencyNode &node = graph_[instance_type_id]; | |
node.initializer_ = [instance_factory](Injector &inj) { | |
auto instance = detail::wrap_into_instance_container(inj.inject(instance_factory)); | |
inj.instance_map_.put<InstanceType>(std::move(instance)); | |
}; | |
node.debug_type_name_ = typeid(typename std::remove_const_t<InstanceType>).name(); | |
node.dependencies_ = { detail::type_id<typename std::remove_const_t<Deps>>()... }; | |
} | |
void DiConfig::toposort_visit_node(int node_id, Injector& injector) | |
{ | |
auto it = graph_.find(node_id); | |
if (it == graph_.end()) { | |
// If there's no node for this type, it means another node depends on this | |
// type, but an instance factory for this type has not been added. | |
// This will result in an injection error later. | |
return; | |
} | |
DependencyNode &node = (*it).second; | |
if (node.mark_ == DependencyNode::Mark::Temp) { | |
throw std::runtime_error(node.debug_type_name_ + " appears to be part of a cycle"); | |
} | |
else if (node.mark_ == DependencyNode::Mark::Unmarked) { | |
node.mark_ = DependencyNode::Mark::Temp; | |
for (int dependency : node.dependencies_) { | |
toposort_visit_node(dependency, injector); | |
} | |
node.mark_ = DependencyNode::Mark::Marked; | |
node.initializer_(injector); | |
} | |
} | |
// Create Instances | |
Injector DiConfig::build_injector() | |
{ | |
Injector injector; | |
for (auto &node : graph_) { | |
// This test is logically redundant, it's just for better performance. | |
if (node.second.mark_ == DependencyNode::Mark::Unmarked) { | |
toposort_visit_node(node.first, injector); | |
} | |
} | |
return injector; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment