Skip to content

Instantly share code, notes, and snippets.

@nonsequitur
Last active June 3, 2019 20:37
Show Gist options
  • Save nonsequitur/a0965cff013c3e0bb780 to your computer and use it in GitHub Desktop.
Save nonsequitur/a0965cff013c3e0bb780 to your computer and use it in GitHub Desktop.
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