Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve runtime performance and clang++ compatibility #2

Merged
merged 6 commits into from Apr 14, 2015
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
@@ -1,7 +1,8 @@
cmake_minimum_required(VERSION 2.6)
project(kangaru-example)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror")
add_executable(kangaru-example example/kangaru-example.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)

install(TARGETS kangaru-example RUNTIME DESTINATION bin)
6 changes: 3 additions & 3 deletions example/kangaru-example.cpp
@@ -1,6 +1,5 @@
#include <iostream>

#include "../include/kangaru.hpp"
#include "kangaru.hpp"

using namespace std;
using namespace kgr;
Expand Down Expand Up @@ -69,7 +68,8 @@ int E::getN() const
return 66;
}

struct MyContainer : Container {
class MyContainer : public Container {
public:
void init() override;
};

Expand Down
94 changes: 57 additions & 37 deletions include/kangaru.hpp
Expand Up @@ -5,6 +5,13 @@
#include <type_traits>
#include <tuple>

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++98-compat"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wweak-vtables"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is needed to remove clang warnings for inlined virtual destructors.

#endif // CLANG

namespace kgr {

template<typename... Types>
Expand Down Expand Up @@ -43,15 +50,15 @@ struct seq_gen<0, S...> {
};


struct Holder {
virtual ~Holder() {

}
class Holder {
public:
virtual ~Holder() = default;
};

template<typename T>
struct InstanceHolder : Holder {
explicit InstanceHolder(std::shared_ptr<T> instance) : _instance{instance} {}
class InstanceHolder final : public Holder {
public:
explicit InstanceHolder(std::shared_ptr<T> instance) : _instance{std::move(instance)} {}

std::shared_ptr<T> getInstance() const {
return _instance;
Expand All @@ -62,15 +69,15 @@ struct InstanceHolder : Holder {
};

template<typename T, typename... Args>
struct CallbackHolder : Holder {
class CallbackHolder final : public Holder {
public:
using callback_t = std::function<std::shared_ptr<T>(Args...)>;

explicit CallbackHolder(callback_t callback) : _callback{callback} {}
explicit CallbackHolder(callback_t callback) : _callback{std::move(callback)} {}

callback_t getCallback() const {
return _callback;
std::shared_ptr<T> operator ()(Args... args) {
return _callback(args...);
}

private:
callback_t _callback;
};
Expand All @@ -81,10 +88,12 @@ std::unique_ptr<T> make_unique( Args&& ...args )
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}

template <typename T> void type_id() {}
using type_id_fn = void(*)();

} // namespace detail

struct Container : std::enable_shared_from_this<Container> {
private:
class Container : public std::enable_shared_from_this<Container> {
template<typename Condition, typename T = void> using enable_if = detail::enable_if_t<Condition::value, T>;
template<typename Condition, typename T = void> using disable_if = detail::enable_if_t<!Condition::value, T>;
template<typename T> using is_service_single = std::is_base_of<Single, Service<T>>;
Expand All @@ -97,43 +106,50 @@ struct Container : std::enable_shared_from_this<Container> {
template<int S, typename T> using parent_element = typename std::tuple_element<S, parent_types<T>>::type;
template<int S, typename Tuple> using tuple_element = typename std::tuple_element<S, Tuple>::type;
using holder_ptr = std::unique_ptr<detail::Holder>;

using holder_cont = std::unordered_map<detail::type_id_fn, holder_ptr>;
public:
Container() = default;
Container(const Container &) = default;
Container(Container &&) = default;
Container& operator =(const Container &) = default;
Container& operator =(Container &&) = default;
virtual ~Container() = default;

template<typename T>
void instance(std::shared_ptr<T> service) {
static_assert(is_service_single<T>::value, "instance only accept Single Service instance.");
call_save_instance(service, tuple_seq<parent_types<T>>());

call_save_instance(std::move(service), tuple_seq<parent_types<T>>{});
}

template<typename T>
void instance() {
static_assert(is_service_single<T>::value, "instance only accept Single Service instance.");

instance(make_service<T>());
}

template<typename T, disable_if<is_abstract<T>>..., disable_if<is_base_of_container<T>>...>
template<typename T, disable_if<is_abstract<T>>* = nullptr, disable_if<is_base_of_container<T>>* = nullptr>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disable_if<...>... does not work with clang.

std::shared_ptr<T> service() {
return get_service<T>();
}

template<typename T, enable_if<is_container<T>>...>
template<typename T, enable_if<is_container<T>>* = nullptr>
std::shared_ptr<T> service() {
return shared_from_this();
}

template<typename T, disable_if<is_container<T>>..., enable_if<is_base_of_container<T>>...>
template<typename T, disable_if<is_container<T>>* = nullptr, enable_if<is_base_of_container<T>>* = nullptr>
std::shared_ptr<T> service() {
return std::dynamic_pointer_cast<T>(shared_from_this());
}

template<typename T, enable_if<is_abstract<T>>...>
template<typename T, enable_if<is_abstract<T>>* = nullptr>
std::shared_ptr<T> service() {
auto it = _services.find(typeid(T).name());
auto it = _services.find(&detail::template type_id<T>);

if (it != _services.end()) {
auto holder = dynamic_cast<detail::InstanceHolder<T>*>(it->second.get());
auto holder = static_cast<detail::InstanceHolder<T>*>(it->second.get());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dynamic_cast is slower.
Since we already know that we have found (see line 149) the InstanceHolder for type T, we can use static_cast here.


if (holder) {
return holder->getInstance();
Expand All @@ -152,17 +168,17 @@ struct Container : std::enable_shared_from_this<Container> {
virtual void init(){}

private:
template<typename T, enable_if<is_service_single<T>>...>
template<typename T, enable_if<is_service_single<T>>* = nullptr>
std::shared_ptr<T> get_service() {
auto it = _services.find(typeid(T).name());
auto it = _services.find(&detail::template type_id<T>);

if (it == _services.end()) {
auto service = make_service<T>();
instance(service);

return service;
} else {
auto holder = dynamic_cast<detail::InstanceHolder<T>*>(it->second.get());
auto holder = static_cast<detail::InstanceHolder<T>*>(it->second.get());

if (holder) {
return holder->getInstance();
Expand All @@ -171,14 +187,14 @@ struct Container : std::enable_shared_from_this<Container> {
return {};
}

template<typename T, disable_if<is_service_single<T>>...>
template<typename T, disable_if<is_service_single<T>>* = nullptr>
std::shared_ptr<T> get_service() {
return make_service<T>();
}

template<typename T, int ...S>
void call_save_instance(std::shared_ptr<T> service, detail::seq<S...>) {
save_instance<T, parent_element<S, T>...>(service);
save_instance<T, parent_element<S, T>...>(std::move(service));
}

template<typename Tuple, int ...S>
Expand All @@ -187,13 +203,13 @@ struct Container : std::enable_shared_from_this<Container> {
}

template<typename T, typename Tuple, int ...S>
std::shared_ptr<T> callback_make_service(detail::seq<S...> seq, Tuple dependencies) const {
auto it = _callbacks.find(typeid(T).name());
std::shared_ptr<T> callback_make_service(detail::seq<S...>, Tuple dependencies) const {
auto it = _callbacks.find(&detail::template type_id<T>);

if (it != _callbacks.end()) {
auto holder = dynamic_cast<detail::CallbackHolder<T, tuple_element<S, Tuple>...>*>(it->second.get());
auto holder = static_cast<detail::CallbackHolder<T, tuple_element<S, Tuple>...>*>(it->second.get());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment for InstanceHolder.

if (holder) {
return holder->getCallback()(std::get<S>(dependencies)...);
return (*holder)(std::get<S>(dependencies)...);
}
}
return {};
Expand All @@ -219,21 +235,21 @@ struct Container : std::enable_shared_from_this<Container> {
template<typename T, typename ...Others>
detail::enable_if_t<(sizeof...(Others) > 0), void> save_instance(std::shared_ptr<T> service) {
save_instance<T>(service);
save_instance<Others...>(service);
save_instance<Others...>(std::move(service));
}

template<typename T>
void save_instance (std::shared_ptr<T> service) {
_services[typeid(T).name()] = detail::make_unique<detail::InstanceHolder<T>>(service);
_services[&detail::template type_id<T>] = detail::make_unique<detail::InstanceHolder<T>>(std::move(service));
}

template<typename T, typename Tuple, int ...S, typename U>
void save_callback (detail::seq<S...>, U callback) {
_callbacks[typeid(T).name()] = detail::make_unique<detail::CallbackHolder<T, std::shared_ptr<tuple_element<S, Tuple>>...>>(callback);
_callbacks[&detail::template type_id<T>] = detail::make_unique<detail::CallbackHolder<T, std::shared_ptr<tuple_element<S, Tuple>>...>>(callback);
}

std::unordered_map<std::string, holder_ptr> _callbacks;
std::unordered_map<std::string, holder_ptr> _services;
holder_cont _callbacks;
holder_cont _services;
};

template<typename T = Container, typename ...Args>
Expand All @@ -247,3 +263,7 @@ std::shared_ptr<T> make_container(Args&& ...args) {
}

} // namespace kgr

#if defined(__clang__)
#pragma clang diagnostic pop
#endif // CLANG