<commit_before>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.

#include "browser/devtools_embedder_message_dispatcher.h"

#include "base/bind.h"
#include "base/values.h"

namespace brightray {

namespace {

using DispatchCallback = DevToolsEmbedderMessageDispatcher::DispatchCallback;

bool GetValue(const base::Value& value, std::string* result) {
  return value.GetAsString(result);
}

bool GetValue(const base::Value& value, int* result) {
  return value.GetAsInteger(result);
}

bool GetValue(const base::Value& value, bool* result) {
  return value.GetAsBoolean(result);
}

bool GetValue(const base::Value& value, gfx::Rect* rect) {
  const base::DictionaryValue* dict;
  if (!value.GetAsDictionary(&dict))
    return false;
  int x = 0;
  int y = 0;
  int width = 0;
  int height = 0;
  if (!dict->GetInteger("x", &x) ||
      !dict->GetInteger("y", &y) ||
      !dict->GetInteger("width", &width) ||
      !dict->GetInteger("height", &height))
    return false;
  rect->SetRect(x, y, width, height);
  return true;
}

template <typename T>
struct StorageTraits {
  using StorageType = T;
};

template <typename T>
struct StorageTraits<const T&> {
  using StorageType = T;
};

template <typename... Ts>
struct ParamTuple {
  bool Parse(const base::ListValue& list,
             const base::ListValue::const_iterator& it) {
    return it == list.end();
  }

  template <typename H, typename... As>
  void Apply(const H& handler, As... args) {
    handler.Run(args...);
  }
};

template <typename T, typename... Ts>
struct ParamTuple<T, Ts...> {
  bool Parse(const base::ListValue& list,
             const base::ListValue::const_iterator& it) {
    return it != list.end() && GetValue(**it, &head) &&
           tail.Parse(list, it + 1);
  }

  template <typename H, typename... As>
  void Apply(const H& handler, As... args) {
    tail.template Apply<H, As..., T>(handler, args..., head);
  }

  typename StorageTraits<T>::StorageType head;
  ParamTuple<Ts...> tail;
};

template<typename... As>
bool ParseAndHandle(const base::Callback<void(As...)>& handler,
                    const DispatchCallback& callback,
                    const base::ListValue& list) {
  ParamTuple<As...> tuple;
  if (!tuple.Parse(list, list.begin()))
    return false;
  tuple.Apply(handler);
  return true;
}

template<typename... As>
bool ParseAndHandleWithCallback(
    const base::Callback<void(const DispatchCallback&, As...)>& handler,
    const DispatchCallback& callback,
    const base::ListValue& list) {
  ParamTuple<As...> tuple;
  if (!tuple.Parse(list, list.begin()))
    return false;
  tuple.Apply(handler, callback);
  return true;
}

}  // namespace

/**
 * Dispatcher for messages sent from the frontend running in an
 * isolated renderer (chrome-devtools:// or chrome://inspect) to the embedder
 * in the browser.
 *
 * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder or
 * chrome.send method accordingly.
 */
class DispatcherImpl : public DevToolsEmbedderMessageDispatcher {
 public:
  ~DispatcherImpl() override {}

  bool Dispatch(const DispatchCallback& callback,
                const std::string& method,
                const base::ListValue* params) override {
    auto it = handlers_.find(method);
    return it != handlers_.end() && it->second.Run(callback, *params);
  }

  template<typename... As>
  void RegisterHandler(const std::string& method,
                       void (Delegate::*handler)(As...),
                       Delegate* delegate) {
    handlers_[method] = base::Bind(&ParseAndHandle<As...>,
                                   base::Bind(handler,
                                              base::Unretained(delegate)));
  }

  template<typename... As>
  void RegisterHandlerWithCallback(
      const std::string& method,
      void (Delegate::*handler)(const DispatchCallback&, As...),
      Delegate* delegate) {
    handlers_[method] = base::Bind(&ParseAndHandleWithCallback<As...>,
                                   base::Bind(handler,
                                              base::Unretained(delegate)));
  }

 private:
  using Handler = base::Callback<bool(const DispatchCallback&,
                                      const base::ListValue&)>;
  using HandlerMap = std::map<std::string, Handler>;
  HandlerMap handlers_;
};

// static
DevToolsEmbedderMessageDispatcher*
DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(
    Delegate* delegate) {
  auto* d = new DispatcherImpl();

  d->RegisterHandler("bringToFront", &Delegate::ActivateWindow, delegate);
  d->RegisterHandler("closeWindow", &Delegate::CloseWindow, delegate);
  d->RegisterHandler("loadCompleted", &Delegate::LoadCompleted, delegate);
  d->RegisterHandler("setInspectedPageBounds",
                     &Delegate::SetInspectedPageBounds, delegate);
  d->RegisterHandler("inspectElementCompleted",
                     &Delegate::InspectElementCompleted, delegate);
  d->RegisterHandler("inspectedURLChanged",
                     &Delegate::InspectedURLChanged, delegate);
  d->RegisterHandlerWithCallback("setIsDocked",
                                 &Delegate::SetIsDocked, delegate);
  d->RegisterHandler("openInNewTab", &Delegate::OpenInNewTab, delegate);
  d->RegisterHandler("save", &Delegate::SaveToFile, delegate);
  d->RegisterHandler("append", &Delegate::AppendToFile, delegate);
  d->RegisterHandler("requestFileSystems",
                     &Delegate::RequestFileSystems, delegate);
  d->RegisterHandler("addFileSystem", &Delegate::AddFileSystem, delegate);
  d->RegisterHandler("removeFileSystem", &Delegate::RemoveFileSystem, delegate);
  d->RegisterHandler("upgradeDraggedFileSystemPermissions",
                     &Delegate::UpgradeDraggedFileSystemPermissions, delegate);
  d->RegisterHandler("indexPath", &Delegate::IndexPath, delegate);
  d->RegisterHandlerWithCallback("loadNetworkResource",
                                 &Delegate::LoadNetworkResource, delegate);
  d->RegisterHandler("stopIndexing", &Delegate::StopIndexing, delegate);
  d->RegisterHandler("searchInPath", &Delegate::SearchInPath, delegate);
  d->RegisterHandler("setWhitelistedShortcuts",
                     &Delegate::SetWhitelistedShortcuts, delegate);
  d->RegisterHandler("zoomIn", &Delegate::ZoomIn, delegate);
  d->RegisterHandler("zoomOut", &Delegate::ZoomOut, delegate);
  d->RegisterHandler("resetZoom", &Delegate::ResetZoom, delegate);
  d->RegisterHandler("setDevicesUpdatesEnabled",
                     &Delegate::SetDevicesUpdatesEnabled, delegate);
  d->RegisterHandler("dispatchProtocolMessage",
                     &Delegate::DispatchProtocolMessageFromDevToolsFrontend,
                     delegate);
  d->RegisterHandler("recordActionUMA", &Delegate::RecordActionUMA, delegate);
  d->RegisterHandlerWithCallback("sendJsonRequest",
                                 &Delegate::SendJsonRequest, delegate);
  d->RegisterHandlerWithCallback("getPreferences",
                                 &Delegate::GetPreferences, delegate);
  d->RegisterHandler("setPreference", &Delegate::SetPreference, delegate);
  d->RegisterHandler("removePreference", &Delegate::RemovePreference, delegate);
  d->RegisterHandler("clearPreferences", &Delegate::ClearPreferences, delegate);
  return d;
}

}  // namespace brightray
<commit_msg>Fix indirection requires pointer operand<commit_after>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-CHROMIUM file.

#include "browser/devtools_embedder_message_dispatcher.h"

#include "base/bind.h"
#include "base/values.h"

namespace brightray {

namespace {

using DispatchCallback = DevToolsEmbedderMessageDispatcher::DispatchCallback;

bool GetValue(const base::Value& value, std::string* result) {
  return value.GetAsString(result);
}

bool GetValue(const base::Value& value, int* result) {
  return value.GetAsInteger(result);
}

bool GetValue(const base::Value& value, bool* result) {
  return value.GetAsBoolean(result);
}

bool GetValue(const base::Value& value, gfx::Rect* rect) {
  const base::DictionaryValue* dict;
  if (!value.GetAsDictionary(&dict))
    return false;
  int x = 0;
  int y = 0;
  int width = 0;
  int height = 0;
  if (!dict->GetInteger("x", &x) ||
      !dict->GetInteger("y", &y) ||
      !dict->GetInteger("width", &width) ||
      !dict->GetInteger("height", &height))
    return false;
  rect->SetRect(x, y, width, height);
  return true;
}

template <typename T>
struct StorageTraits {
  using StorageType = T;
};

template <typename T>
struct StorageTraits<const T&> {
  using StorageType = T;
};

template <typename... Ts>
struct ParamTuple {
  bool Parse(const base::ListValue& list,
             const base::ListValue::const_iterator& it) {
    return it == list.end();
  }

  template <typename H, typename... As>
  void Apply(const H& handler, As... args) {
    handler.Run(args...);
  }
};

template <typename T, typename... Ts>
struct ParamTuple<T, Ts...> {
  bool Parse(const base::ListValue& list,
             const base::ListValue::const_iterator& it) {
    return it != list.end() && GetValue(*it, &head) &&
           tail.Parse(list, it + 1);
  }

  template <typename H, typename... As>
  void Apply(const H& handler, As... args) {
    tail.template Apply<H, As..., T>(handler, args..., head);
  }

  typename StorageTraits<T>::StorageType head;
  ParamTuple<Ts...> tail;
};

template<typename... As>
bool ParseAndHandle(const base::Callback<void(As...)>& handler,
                    const DispatchCallback& callback,
                    const base::ListValue& list) {
  ParamTuple<As...> tuple;
  if (!tuple.Parse(list, list.begin()))
    return false;
  tuple.Apply(handler);
  return true;
}

template<typename... As>
bool ParseAndHandleWithCallback(
    const base::Callback<void(const DispatchCallback&, As...)>& handler,
    const DispatchCallback& callback,
    const base::ListValue& list) {
  ParamTuple<As...> tuple;
  if (!tuple.Parse(list, list.begin()))
    return false;
  tuple.Apply(handler, callback);
  return true;
}

}  // namespace

/**
 * Dispatcher for messages sent from the frontend running in an
 * isolated renderer (chrome-devtools:// or chrome://inspect) to the embedder
 * in the browser.
 *
 * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder or
 * chrome.send method accordingly.
 */
class DispatcherImpl : public DevToolsEmbedderMessageDispatcher {
 public:
  ~DispatcherImpl() override {}

  bool Dispatch(const DispatchCallback& callback,
                const std::string& method,
                const base::ListValue* params) override {
    auto it = handlers_.find(method);
    return it != handlers_.end() && it->second.Run(callback, *params);
  }

  template<typename... As>
  void RegisterHandler(const std::string& method,
                       void (Delegate::*handler)(As...),
                       Delegate* delegate) {
    handlers_[method] = base::Bind(&ParseAndHandle<As...>,
                                   base::Bind(handler,
                                              base::Unretained(delegate)));
  }

  template<typename... As>
  void RegisterHandlerWithCallback(
      const std::string& method,
      void (Delegate::*handler)(const DispatchCallback&, As...),
      Delegate* delegate) {
    handlers_[method] = base::Bind(&ParseAndHandleWithCallback<As...>,
                                   base::Bind(handler,
                                              base::Unretained(delegate)));
  }

 private:
  using Handler = base::Callback<bool(const DispatchCallback&,
                                      const base::ListValue&)>;
  using HandlerMap = std::map<std::string, Handler>;
  HandlerMap handlers_;
};

// static
DevToolsEmbedderMessageDispatcher*
DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(
    Delegate* delegate) {
  auto* d = new DispatcherImpl();

  d->RegisterHandler("bringToFront", &Delegate::ActivateWindow, delegate);
  d->RegisterHandler("closeWindow", &Delegate::CloseWindow, delegate);
  d->RegisterHandler("loadCompleted", &Delegate::LoadCompleted, delegate);
  d->RegisterHandler("setInspectedPageBounds",
                     &Delegate::SetInspectedPageBounds, delegate);
  d->RegisterHandler("inspectElementCompleted",
                     &Delegate::InspectElementCompleted, delegate);
  d->RegisterHandler("inspectedURLChanged",
                     &Delegate::InspectedURLChanged, delegate);
  d->RegisterHandlerWithCallback("setIsDocked",
                                 &Delegate::SetIsDocked, delegate);
  d->RegisterHandler("openInNewTab", &Delegate::OpenInNewTab, delegate);
  d->RegisterHandler("save", &Delegate::SaveToFile, delegate);
  d->RegisterHandler("append", &Delegate::AppendToFile, delegate);
  d->RegisterHandler("requestFileSystems",
                     &Delegate::RequestFileSystems, delegate);
  d->RegisterHandler("addFileSystem", &Delegate::AddFileSystem, delegate);
  d->RegisterHandler("removeFileSystem", &Delegate::RemoveFileSystem, delegate);
  d->RegisterHandler("upgradeDraggedFileSystemPermissions",
                     &Delegate::UpgradeDraggedFileSystemPermissions, delegate);
  d->RegisterHandler("indexPath", &Delegate::IndexPath, delegate);
  d->RegisterHandlerWithCallback("loadNetworkResource",
                                 &Delegate::LoadNetworkResource, delegate);
  d->RegisterHandler("stopIndexing", &Delegate::StopIndexing, delegate);
  d->RegisterHandler("searchInPath", &Delegate::SearchInPath, delegate);
  d->RegisterHandler("setWhitelistedShortcuts",
                     &Delegate::SetWhitelistedShortcuts, delegate);
  d->RegisterHandler("zoomIn", &Delegate::ZoomIn, delegate);
  d->RegisterHandler("zoomOut", &Delegate::ZoomOut, delegate);
  d->RegisterHandler("resetZoom", &Delegate::ResetZoom, delegate);
  d->RegisterHandler("setDevicesUpdatesEnabled",
                     &Delegate::SetDevicesUpdatesEnabled, delegate);
  d->RegisterHandler("dispatchProtocolMessage",
                     &Delegate::DispatchProtocolMessageFromDevToolsFrontend,
                     delegate);
  d->RegisterHandler("recordActionUMA", &Delegate::RecordActionUMA, delegate);
  d->RegisterHandlerWithCallback("sendJsonRequest",
                                 &Delegate::SendJsonRequest, delegate);
  d->RegisterHandlerWithCallback("getPreferences",
                                 &Delegate::GetPreferences, delegate);
  d->RegisterHandler("setPreference", &Delegate::SetPreference, delegate);
  d->RegisterHandler("removePreference", &Delegate::RemovePreference, delegate);
  d->RegisterHandler("clearPreferences", &Delegate::ClearPreferences, delegate);
  return d;
}

}  // namespace brightray
<|endoftext|>


<commit_before>/**
  * @file GeneticStrategy.hpp
  * @brief genetic strategy interface
  */
#ifndef _BACKEND_GENETIC_GENETICSTRATEGY_HPP
#define _BACKEND_GENETIC_GENETICSTRATEGY_HPP

#include <stdint.h>

#include "ClusterEncoder.hpp"
#include "AbstractGraph.hpp"

namespace backend {
namespace genetic {

/**
 * @class GeneticStrategy
 * @brief interface for objects which implement genetic algorithms.
 * GeneticStrategy is a facade around its multiple subcomponents
 */
class GeneticStrategy {
 public:
     /**
      * @brief create a GeneticStrategy based on a graph
      * @param g the graph on which the strategy is based
      */
     GeneticStrategy(const AbstractGraph& g);
     /**
      * @brief obtain the next generation of the clustering solution
      * @return the order of the new generation
      */
     virtual uint64_t runGeneticIteration() = 0;

     /**
      * @brief get the current clustering solution
      * @return the current clustering solution
      */
     virtual CluterEncoder::Encoding getClusteringSolution() = 0;

     /**
      * @brief standard destructor
      */
     virtual ~GeneticStrategy();
};

}  // namespace genetic
}  // namespace backend

#endif  // _BACKEND_GENETIC_GENETICSTRATEGY_HPP

<commit_msg>fixed typo<commit_after>/**
  * @file GeneticStrategy.hpp
  * @brief genetic strategy interface
  */
#ifndef _BACKEND_GENETIC_GENETICSTRATEGY_HPP
#define _BACKEND_GENETIC_GENETICSTRATEGY_HPP

#include <stdint.h>

#include "ClusterEncoder.hpp"
#include "AbstractGraph.hpp"

namespace backend {
namespace genetic {

/**
 * @class GeneticStrategy
 * @brief interface for objects which implement genetic algorithms.
 * GeneticStrategy is a facade around its multiple subcomponents
 */
class GeneticStrategy {
 public:
     /**
      * @brief create a GeneticStrategy based on a graph
      * @param g the graph on which the strategy is based
      */
     GeneticStrategy(const AbstractGraph& g);
     /**
      * @brief obtain the next generation of the clustering solution
      * @return the order of the new generation
      */
     virtual uint64_t runGeneticIteration() = 0;

     /**
      * @brief get the current clustering solution
      * @return the current clustering solution
      */
     virtual ClusterEncoder::Encoding getClusteringSolution() = 0;

     /**
      * @brief standard destructor
      */
     virtual ~GeneticStrategy();
};

}  // namespace genetic
}  // namespace backend

#endif  // _BACKEND_GENETIC_GENETICSTRATEGY_HPP

<|endoftext|>


<commit_before>#ifndef APOLLO_FUNCTION_HPP_INCLUDED
#define APOLLO_FUNCTION_HPP_INCLUDED APOLLO_FUNCTION_HPP_INCLUDED

#include <apollo/config.hpp>
#include <apollo/lapi.hpp>
#include <apollo/make_function.hpp>
#include <apollo/reference.hpp>
#include <apollo/stack_balance.hpp>

#include <boost/type_index.hpp>

namespace apollo {

namespace detail {

APOLLO_API boost::typeindex::type_info const& function_type(
    lua_State* L, int idx);

// function_converter //

template <typename F, typename Enable=void>
struct function_converter;

template <template<class> class FObj, typename R, typename... Args>
struct function_converter<FObj<R(Args...)>> {
    using type = FObj<R(Args...)>;

    static type from_stack(lua_State* L, int idx)
    {
        auto const& fty = function_type(L, idx);
        if (fty == boost::typeindex::type_id<type>()) {
            stack_balance balance(L);
            BOOST_VERIFY(lua_getupvalue(L, idx, detail::fn_upval_fn));
            BOOST_ASSERT(lua_type(L, -1) == LUA_TUSERDATA); // Not light!
            return *static_cast<type*>(lua_touserdata(L, -1));
        }

        // Plain function pointer in Lua? Then construct from it.
        using plainfconv = function_converter<R(*)(Args...)>;
        if (fty == boost::typeindex::type_id<typename plainfconv::type>())
            return plainfconv::from_stack(L, idx);

        // TODO?: optimization: Before falling back to the pcall lambda,
        // try boost::function and std::function.

        registry_reference luaFunction(L, idx, ref_mode::copy);
        return [luaFunction](Args... args) -> R {
            lua_State* L_ = luaFunction.L();
            stack_balance b(L_);
            luaFunction.push();
            // Use push_impl to allow empty Args.
            int const n_args = push_impl(L_, std::forward<Args>(args)...);
            pcall(L_, n_args, 1);
            return apollo::from_stack<R>(L_, -1);
        };
    }

    static unsigned n_conversion_steps(lua_State* L, int idx)
    {
        if (lua_isfunction(L, idx))
            return 0;
        if (luaL_getmetafield(L, idx, "__call")) { // TODO: pcall
            lua_pop(L, 1);
            return 2;
        }
        return no_conversion;
    }
};

template <typename F>
struct function_converter<F, typename std::enable_if<
        detail::is_plain_function<F>::value ||
        std::is_member_function_pointer<F>::value>::type>
{
    using type = F;

    using is_light = is_light_function<F>;

    static F from_stack(lua_State* L, int idx)
    {
        stack_balance balance(L);
        BOOST_VERIFY(lua_getupvalue(L, idx, fn_upval_fn));
        BOOST_ASSERT(lua_isuserdata(L, -1));
        void* ud = lua_touserdata(L, -1);
        return from_stack_impl(ud, is_light());
    }

    static unsigned n_conversion_steps(lua_State* L, int idx)
    {
        return function_type(L, idx) == boost::typeindex::type_id<type>() ?
            0 : no_conversion;
    }

private:
    // Light function:
    static F from_stack_impl(void* ud, std::true_type)
    {
        return reinterpret_cast<light_function_holder<F>&>(ud).f;
    }

    // Non-light function:
    static F& from_stack_impl(void* ud, std::false_type)
    {
        return *static_cast<F*>(ud);
    }
};

} // namespace detail

// Function converter //
template<typename T>
struct converter<T, typename std::enable_if<
        detail::lua_type_id<T>::value == LUA_TFUNCTION>::type>
    : converter_base<T> {

private:
    // Work around a MSVC 12 (2013) bug, where
    // decltype(&fn_templateX<>) == decltype(fn_template<T>)
    // (another workaround would be to wrap the & in a function).
    using fn_t = typename std::conditional<
        std::is_function<T>::value, T*, T>::type;

    using fconverter = detail::function_converter<fn_t>;

public:
    static int push(lua_State* L, fn_t const& f)
    {
        apollo::push(L, make_function(f));
        return 1;
    }

    static unsigned n_conversion_steps(lua_State* L, int idx)
    {
        return fconverter::n_conversion_steps(L, idx);
    }

    static typename fconverter::type from_stack(lua_State* L, int idx)
    {
        return fconverter::from_stack(L, idx);
    }
};

} // namespace apollo

#endif // APOLLO_FUNCTION_HPP_INCLUDED
<commit_msg>Lua function wrapper works w/ LUA_MULTRET.<commit_after>#ifndef APOLLO_FUNCTION_HPP_INCLUDED
#define APOLLO_FUNCTION_HPP_INCLUDED APOLLO_FUNCTION_HPP_INCLUDED

#include <apollo/config.hpp>
#include <apollo/lapi.hpp>
#include <apollo/make_function.hpp>
#include <apollo/reference.hpp>
#include <apollo/stack_balance.hpp>

#include <boost/type_index.hpp>

namespace apollo {

namespace detail {

APOLLO_API boost::typeindex::type_info const& function_type(
    lua_State* L, int idx);

// function_converter //

template <typename F, typename Enable=void>
struct function_converter;

template <template<class> class FObj, typename R, typename... Args>
struct function_converter<FObj<R(Args...)>> {
    using type = FObj<R(Args...)>;

    static type from_stack(lua_State* L, int idx)
    {
        auto const& fty = function_type(L, idx);
        if (fty == boost::typeindex::type_id<type>()) {
            stack_balance balance(L);
            BOOST_VERIFY(lua_getupvalue(L, idx, detail::fn_upval_fn));
            BOOST_ASSERT(lua_type(L, -1) == LUA_TUSERDATA); // Not light!
            return *static_cast<type*>(lua_touserdata(L, -1));
        }

        // Plain function pointer in Lua? Then construct from it.
        using plainfconv = function_converter<R(*)(Args...)>;
        if (fty == boost::typeindex::type_id<typename plainfconv::type>())
            return plainfconv::from_stack(L, idx);

        // TODO?: optimization: Before falling back to the pcall lambda,
        // try boost::function and std::function.

        registry_reference luaFunction(L, idx, ref_mode::copy);
        return [luaFunction](Args... args) -> R {
            lua_State* L_ = luaFunction.L();
            stack_balance b(L_);
            int const oldtop = lua_gettop(L_);
            luaFunction.push();
            int const n_args = push_impl(L_, std::forward<Args>(args)...);
            pcall(L_, n_args, LUA_MULTRET);
            return apollo::from_stack<R>(L_, oldtop + 1);
        };
    }

    static unsigned n_conversion_steps(lua_State* L, int idx)
    {
        if (lua_isfunction(L, idx))
            return 0;
        if (luaL_getmetafield(L, idx, "__call")) { // TODO: pcall
            lua_pop(L, 1);
            return 2;
        }
        return no_conversion;
    }
};

template <typename F>
struct function_converter<F, typename std::enable_if<
        detail::is_plain_function<F>::value ||
        std::is_member_function_pointer<F>::value>::type>
{
    using type = F;

    using is_light = is_light_function<F>;

    static F from_stack(lua_State* L, int idx)
    {
        stack_balance balance(L);
        BOOST_VERIFY(lua_getupvalue(L, idx, fn_upval_fn));
        BOOST_ASSERT(lua_isuserdata(L, -1));
        void* ud = lua_touserdata(L, -1);
        return from_stack_impl(ud, is_light());
    }

    static unsigned n_conversion_steps(lua_State* L, int idx)
    {
        return function_type(L, idx) == boost::typeindex::type_id<type>() ?
            0 : no_conversion;
    }

private:
    // Light function:
    static F from_stack_impl(void* ud, std::true_type)
    {
        return reinterpret_cast<light_function_holder<F>&>(ud).f;
    }

    // Non-light function:
    static F& from_stack_impl(void* ud, std::false_type)
    {
        return *static_cast<F*>(ud);
    }
};

} // namespace detail

// Function converter //
template<typename T>
struct converter<T, typename std::enable_if<
        detail::lua_type_id<T>::value == LUA_TFUNCTION>::type>
    : converter_base<T> {

private:
    // Work around a MSVC 12 (2013) bug, where
    // decltype(&fn_templateX<>) == decltype(fn_template<T>)
    // (another workaround would be to wrap the & in a function).
    using fn_t = typename std::conditional<
        std::is_function<T>::value, T*, T>::type;

    using fconverter = detail::function_converter<fn_t>;

public:
    static int push(lua_State* L, fn_t const& f)
    {
        apollo::push(L, make_function(f));
        return 1;
    }

    static unsigned n_conversion_steps(lua_State* L, int idx)
    {
        return fconverter::n_conversion_steps(L, idx);
    }

    static typename fconverter::type from_stack(lua_State* L, int idx)
    {
        return fconverter::from_stack(L, idx);
    }
};

} // namespace apollo

#endif // APOLLO_FUNCTION_HPP_INCLUDED
<|endoftext|>


<commit_before>#ifndef CTOOL_EXTRACT
#define CTOOL_EXTRACT

#include "ToolBase.hpp"
#include <stdio.h>

class ToolExtract final : public ToolBase
{
    HECL::Database::IDataSpec::ExtractPassInfo m_einfo;
    struct SpecExtractPass
    {
        const HECL::Database::DataSpecEntry* m_entry;
        std::unique_ptr<HECL::Database::IDataSpec> m_instance;
        SpecExtractPass(const HECL::Database::DataSpecEntry* entry, HECL::Database::IDataSpec* instance)
        : m_entry(entry), m_instance(instance) {}
        SpecExtractPass(const SpecExtractPass& other) = delete;
        SpecExtractPass(SpecExtractPass&& other) = default;
    };
    std::vector<SpecExtractPass> m_specPasses;
    std::vector<HECL::Database::IDataSpec::ExtractReport> m_reps;
    std::unique_ptr<HECL::Database::Project> m_fallbackProj;
    HECL::Database::Project* m_useProj;
public:
    ToolExtract(const ToolPassInfo& info)
    : ToolBase(info)
    {
        if (!m_info.args.size())
            LogModule.report(LogVisor::FatalError, "hecl extract needs a source path as its first argument");

        if (!info.project)
        {
            /* Get name from input file and init project there */
            HECL::SystemString baseFile = info.args[0];
            size_t slashPos = baseFile.rfind(_S('/'));
            if (slashPos == HECL::SystemString::npos)
                slashPos = baseFile.rfind(_S('\\'));
            if (slashPos != HECL::SystemString::npos)
                baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end());
            size_t dotPos = baseFile.rfind(_S('.'));
            if (dotPos != HECL::SystemString::npos)
                baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);

            if (baseFile.empty())
                LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory");

            size_t ErrorRef = LogVisor::ErrorCount;
            HECL::SystemString rootDir = info.cwd + _S('/') + baseFile;
            HECL::ProjectRootPath newProjRoot(rootDir);
            newProjRoot.makeDir();
            m_fallbackProj.reset(new HECL::Database::Project(newProjRoot));
            if (LogVisor::ErrorCount > ErrorRef)
                LogModule.report(LogVisor::FatalError, "unable to init project at '%s'", rootDir.c_str());
            LogModule.report(LogVisor::Info, _S("initialized project at '%s/.hecl'"), rootDir.c_str());
            m_useProj = m_fallbackProj.get();
        }
        else
            m_useProj = info.project;

        m_einfo.srcpath = m_info.args[0];
        m_einfo.extractArgs.reserve(info.args.size() - 1);
        m_einfo.force = info.force;
        for (std::vector<HECL::SystemString>::const_iterator it=info.args.begin() + 1;
             it != info.args.end();
             ++it)
            m_einfo.extractArgs.push_back(*it);

        for (const HECL::Database::DataSpecEntry* entry : HECL::Database::DATA_SPEC_REGISTRY)
        {
            HECL::Database::IDataSpec* ds = entry->m_factory(*m_useProj, HECL::Database::TOOL_EXTRACT);
            if (ds)
            {
                if (ds->canExtract(m_einfo, m_reps))
                    m_specPasses.emplace_back(entry, ds);
                else
                    delete ds;
            }
        }
    }

    static void Help(HelpOutput& help)
    {
        help.secHead(_S("NAME"));
        help.beginWrap();
        help.wrap(_S("hecl-extract - Extract objects from supported package/image formats\n"));
        help.endWrap();

        help.secHead(_S("SYNOPSIS"));
        help.beginWrap();
        help.wrap(_S("hecl extract <packagefile> [<subnode>...]\n"));
        help.endWrap();

        help.secHead(_S("DESCRIPTION"));
        help.beginWrap();
        help.wrap(_S("This command recursively extracts all or part of a dataspec-supported ")
                  _S("package format. Each object is decoded to a working format and added to the project.\n\n"));
        help.endWrap();

        help.secHead(_S("OPTIONS"));
        help.optionHead(_S("<packagefile>[/<subnode>...]"), _S("input file"));
        help.beginWrap();
        help.wrap(_S("Specifies the package file or disc image to source data from. ")
                  _S("An optional subnode specifies a named hierarchical-node specific ")
                  _S("to the game architecture (levels/areas)."));
        help.endWrap();
    }

    HECL::SystemString toolName() const {return _S("extract");}

    static void _recursivePrint(int level, HECL::Database::IDataSpec::ExtractReport& rep)
    {
        for (int l=0 ; l<level ; ++l)
            HECL::Printf(_S("  "));
#if _WIN32
        HECL::Printf(_S("%s"), rep.name.c_str());
#else
        if (XTERM_COLOR)
            HECL::Printf(_S("" BOLD "%s" NORMAL ""), rep.name.c_str());
        else
            HECL::Printf(_S("%s"), rep.name.c_str());
#endif
        if (rep.desc.size())
            HECL::Printf(_S(" [%s]"), rep.desc.c_str());
        HECL::Printf(_S("\n"));
        for (HECL::Database::IDataSpec::ExtractReport& child : rep.childOpts)
            _recursivePrint(level + 1, child);
    }

    int run()
    {
        if (m_specPasses.empty())
        {
#if _WIN32
            HECL::Printf(_S("NOTHING TO EXTRACT\n"));
#else
            if (XTERM_COLOR)
                HECL::Printf(_S("" RED BOLD "NOTHING TO EXTRACT" NORMAL "\n"));
            else
                HECL::Printf(_S("NOTHING TO EXTRACT\n"));
#endif
            return -1;
        }

#if _WIN32
        HECL::Printf(_S("ABOUT TO EXTRACT:\n"));
#else
        if (XTERM_COLOR)
            HECL::Printf(_S("" GREEN BOLD "ABOUT TO EXTRACT:" NORMAL "\n"));
        else
            HECL::Printf(_S("ABOUT TO EXTRACT:\n"));
#endif

        for (HECL::Database::IDataSpec::ExtractReport& rep : m_reps)
        {
            _recursivePrint(0, rep);
            HECL::Printf(_S("\n"));
        }

#if _WIN32
        HECL::Printf(_S("\nContinue? (Y/N) "));
#else
        if (XTERM_COLOR)
            HECL::Printf(_S("\n" BLUE BOLD "Continue?" NORMAL " (Y/N) "));
        else
            HECL::Printf(_S("\nContinue? (Y/N) "));
#endif

        int ch;
        while ((ch = getchar()))
        {
            if (ch == 'n' || ch == 'N')
                return 0;
            if (ch == 'y' || ch == 'Y')
                break;
        }

        for (SpecExtractPass& ds : m_specPasses)
        {
#if _WIN32
            HECL::Printf(_S("Using DataSpec %s:\n"), ds.m_entry->m_name);
#else
            if (XTERM_COLOR)
                HECL::Printf(_S("" MAGENTA BOLD "Using DataSpec %s:" NORMAL "\n"), ds.m_entry->m_name);
            else
                HECL::Printf(_S("Using DataSpec %s:\n"), ds.m_entry->m_name);
#endif

            int lineIdx = 0;
            int prevIFactor = -1;
            ds.m_instance->doExtract(m_einfo,
                                     [&lineIdx, &prevIFactor](const HECL::SystemChar* message, int lidx, float factor)
            {
                int iFactor = factor * 100.0;
                if (iFactor == prevIFactor)
                    return;
                prevIFactor = iFactor;
#ifndef _WIN32
                if (XTERM_COLOR)
                    HECL::Printf(_S("" HIDE_CURSOR ""));
#endif

                if (lidx > lineIdx)
                {
                    HECL::Printf(_S("\n  "));
                    lineIdx = lidx;
                }
                else
                    HECL::Printf(_S("  "));

                int width = HECL::ConsoleWidth();
                int half = width / 2 - 2;

                size_t messageLen = HECL::StrLen(message);
                if (messageLen > half)
                    HECL::Printf(_S("%.*s... "), half-3, message);
                else
                {
                    HECL::Printf(_S("%s"), message);
                    for (int i=half-messageLen ; i>=0 ; --i)
                        HECL::Printf(_S(" "));
                }

#ifndef _WIN32
                if (XTERM_COLOR)
                {
                    size_t blocks = half - 7;
                    size_t filled = blocks * factor;
                    size_t rem = blocks - filled;
                    HECL::Printf(_S("" BOLD "%3d%% ["), iFactor);
                    for (int b=0 ; b<filled ; ++b)
                        HECL::Printf(_S("#"), message);
                    for (int b=0 ; b<rem ; ++b)
                        HECL::Printf(_S("-"), message);
                    HECL::Printf(_S("]" NORMAL ""));
                }
                else
                {
#endif
                    size_t blocks = half - 7;
                    size_t filled = blocks * factor;
                    size_t rem = blocks - filled;
                    HECL::Printf(_S("%3d%% ["), iFactor);
                    for (int b=0 ; b<filled ; ++b)
                        HECL::Printf(_S("#"), message);
                    for (int b=0 ; b<rem ; ++b)
                        HECL::Printf(_S("-"), message);
                    HECL::Printf(_S("]"));
#ifndef _WIN32
                }
#endif

                HECL::Printf(_S("\r"));
#ifndef _WIN32
                if (XTERM_COLOR)
                    HECL::Printf(_S("" SHOW_CURSOR ""));
#endif
                fflush(stdout);
            });
            HECL::Printf(_S("\n\n"));
        }

        return 0;
    }
};

#endif // CTOOL_EXTRACT
<commit_msg>Added nicer (Y/n) prompt<commit_after>#ifndef CTOOL_EXTRACT
#define CTOOL_EXTRACT

#include "ToolBase.hpp"
#include <stdio.h>
#include <conio.h>

class ToolExtract final : public ToolBase
{
    HECL::Database::IDataSpec::ExtractPassInfo m_einfo;
    struct SpecExtractPass
    {
        const HECL::Database::DataSpecEntry* m_entry;
        std::unique_ptr<HECL::Database::IDataSpec> m_instance;
        SpecExtractPass(const HECL::Database::DataSpecEntry* entry, HECL::Database::IDataSpec* instance)
        : m_entry(entry), m_instance(instance) {}
        SpecExtractPass(const SpecExtractPass& other) = delete;
        SpecExtractPass(SpecExtractPass&& other) = default;
    };
    std::vector<SpecExtractPass> m_specPasses;
    std::vector<HECL::Database::IDataSpec::ExtractReport> m_reps;
    std::unique_ptr<HECL::Database::Project> m_fallbackProj;
    HECL::Database::Project* m_useProj;
public:
    ToolExtract(const ToolPassInfo& info)
    : ToolBase(info)
    {
        if (!m_info.args.size())
            LogModule.report(LogVisor::FatalError, "hecl extract needs a source path as its first argument");

        if (!info.project)
        {
            /* Get name from input file and init project there */
            HECL::SystemString baseFile = info.args[0];
            size_t slashPos = baseFile.rfind(_S('/'));
            if (slashPos == HECL::SystemString::npos)
                slashPos = baseFile.rfind(_S('\\'));
            if (slashPos != HECL::SystemString::npos)
                baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end());
            size_t dotPos = baseFile.rfind(_S('.'));
            if (dotPos != HECL::SystemString::npos)
                baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);

            if (baseFile.empty())
                LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory");

            size_t ErrorRef = LogVisor::ErrorCount;
            HECL::SystemString rootDir = info.cwd + _S('/') + baseFile;
            HECL::ProjectRootPath newProjRoot(rootDir);
            newProjRoot.makeDir();
            m_fallbackProj.reset(new HECL::Database::Project(newProjRoot));
            if (LogVisor::ErrorCount > ErrorRef)
                LogModule.report(LogVisor::FatalError, "unable to init project at '%s'", rootDir.c_str());
            LogModule.report(LogVisor::Info, _S("initialized project at '%s/.hecl'"), rootDir.c_str());
            m_useProj = m_fallbackProj.get();
        }
        else
            m_useProj = info.project;

        m_einfo.srcpath = m_info.args[0];
        m_einfo.extractArgs.reserve(info.args.size() - 1);
        m_einfo.force = info.force;
        for (std::vector<HECL::SystemString>::const_iterator it=info.args.begin() + 1;
             it != info.args.end();
             ++it)
            m_einfo.extractArgs.push_back(*it);

        for (const HECL::Database::DataSpecEntry* entry : HECL::Database::DATA_SPEC_REGISTRY)
        {
            HECL::Database::IDataSpec* ds = entry->m_factory(*m_useProj, HECL::Database::TOOL_EXTRACT);
            if (ds)
            {
                if (ds->canExtract(m_einfo, m_reps))
                    m_specPasses.emplace_back(entry, ds);
                else
                    delete ds;
            }
        }
    }

    static void Help(HelpOutput& help)
    {
        help.secHead(_S("NAME"));
        help.beginWrap();
        help.wrap(_S("hecl-extract - Extract objects from supported package/image formats\n"));
        help.endWrap();

        help.secHead(_S("SYNOPSIS"));
        help.beginWrap();
        help.wrap(_S("hecl extract <packagefile> [<subnode>...]\n"));
        help.endWrap();

        help.secHead(_S("DESCRIPTION"));
        help.beginWrap();
        help.wrap(_S("This command recursively extracts all or part of a dataspec-supported ")
                  _S("package format. Each object is decoded to a working format and added to the project.\n\n"));
        help.endWrap();

        help.secHead(_S("OPTIONS"));
        help.optionHead(_S("<packagefile>[/<subnode>...]"), _S("input file"));
        help.beginWrap();
        help.wrap(_S("Specifies the package file or disc image to source data from. ")
                  _S("An optional subnode specifies a named hierarchical-node specific ")
                  _S("to the game architecture (levels/areas)."));
        help.endWrap();
    }

    HECL::SystemString toolName() const {return _S("extract");}

    static void _recursivePrint(int level, HECL::Database::IDataSpec::ExtractReport& rep)
    {
        for (int l=0 ; l<level ; ++l)
            HECL::Printf(_S("  "));
#if _WIN32
        HECL::Printf(_S("%s"), rep.name.c_str());
#else
        if (XTERM_COLOR)
            HECL::Printf(_S("" BOLD "%s" NORMAL ""), rep.name.c_str());
        else
            HECL::Printf(_S("%s"), rep.name.c_str());
#endif
        if (rep.desc.size())
            HECL::Printf(_S(" [%s]"), rep.desc.c_str());
        HECL::Printf(_S("\n"));
        for (HECL::Database::IDataSpec::ExtractReport& child : rep.childOpts)
            _recursivePrint(level + 1, child);
    }

    int run()
    {
        if (m_specPasses.empty())
        {
#if _WIN32
            HECL::Printf(_S("NOTHING TO EXTRACT\n"));
#else
            if (XTERM_COLOR)
                HECL::Printf(_S("" RED BOLD "NOTHING TO EXTRACT" NORMAL "\n"));
            else
                HECL::Printf(_S("NOTHING TO EXTRACT\n"));
#endif
            return -1;
        }

#if _WIN32
        HECL::Printf(_S("ABOUT TO EXTRACT:\n"));
#else
        if (XTERM_COLOR)
            HECL::Printf(_S("" GREEN BOLD "ABOUT TO EXTRACT:" NORMAL "\n"));
        else
            HECL::Printf(_S("ABOUT TO EXTRACT:\n"));
#endif

        for (HECL::Database::IDataSpec::ExtractReport& rep : m_reps)
        {
            _recursivePrint(0, rep);
            HECL::Printf(_S("\n"));
        }

#if _WIN32
        HECL::Printf(_S("\nContinue? (Y/n) "));
#else
        if (XTERM_COLOR)
            HECL::Printf(_S("\n" BLUE BOLD "Continue?" NORMAL " (Y/N) "));
        else
            HECL::Printf(_S("\nContinue? (Y/N) "));
#endif

        int ch;
        while ((ch = getch()))
        {
            if (ch == 'n' || ch == 'N')
                return 0;
            if (ch == 'y' || ch == 'Y' || ch == 13)
                break;
        }
        HECL::Printf(_S("\n"));

        for (SpecExtractPass& ds : m_specPasses)
        {
#if _WIN32
            HECL::Printf(_S("Using DataSpec %s:\n"), ds.m_entry->m_name);
#else
            if (XTERM_COLOR)
                HECL::Printf(_S("" MAGENTA BOLD "Using DataSpec %s:" NORMAL "\n"), ds.m_entry->m_name);
            else
                HECL::Printf(_S("Using DataSpec %s:\n"), ds.m_entry->m_name);
#endif

            int lineIdx = 0;
            int prevIFactor = -1;
            ds.m_instance->doExtract(m_einfo,
                                     [&lineIdx, &prevIFactor](const HECL::SystemChar* message, int lidx, float factor)
            {
                int iFactor = factor * 100.0;
                if (iFactor == prevIFactor)
                    return;
                prevIFactor = iFactor;
#ifndef _WIN32
                if (XTERM_COLOR)
                    HECL::Printf(_S("" HIDE_CURSOR ""));
#endif

                if (lidx > lineIdx)
                {
                    HECL::Printf(_S("\n  "));
                    lineIdx = lidx;
                }
                else
                    HECL::Printf(_S("  "));

                int width = HECL::ConsoleWidth();
                int half = width / 2 - 2;

                size_t messageLen = HECL::StrLen(message);
                if (messageLen > half)
                    HECL::Printf(_S("%.*s... "), half-3, message);
                else
                {
                    HECL::Printf(_S("%s"), message);
                    for (int i=half-messageLen ; i>=0 ; --i)
                        HECL::Printf(_S(" "));
                }

#ifndef _WIN32
                if (XTERM_COLOR)
                {
                    size_t blocks = half - 7;
                    size_t filled = blocks * factor;
                    size_t rem = blocks - filled;
                    HECL::Printf(_S("" BOLD "%3d%% ["), iFactor);
                    for (int b=0 ; b<filled ; ++b)
                        HECL::Printf(_S("#"), message);
                    for (int b=0 ; b<rem ; ++b)
                        HECL::Printf(_S("-"), message);
                    HECL::Printf(_S("]" NORMAL ""));
                }
                else
                {
#endif
                    size_t blocks = half - 7;
                    size_t filled = blocks * factor;
                    size_t rem = blocks - filled;
                    HECL::Printf(_S("%3d%% ["), iFactor);
                    for (int b=0 ; b<filled ; ++b)
                        HECL::Printf(_S("#"), message);
                    for (int b=0 ; b<rem ; ++b)
                        HECL::Printf(_S("-"), message);
                    HECL::Printf(_S("]"));
#ifndef _WIN32
                }
#endif

                HECL::Printf(_S("\r"));
#ifndef _WIN32
                if (XTERM_COLOR)
                    HECL::Printf(_S("" SHOW_CURSOR ""));
#endif
                fflush(stdout);
            });
            HECL::Printf(_S("\n\n"));
        }

        return 0;
    }
};

#endif // CTOOL_EXTRACT
<|endoftext|>


<commit_before>
#pragma once

#include <cinatra/http_server.hpp>
#include <cinatra/http_router.hpp>
#include <cinatra/request.hpp>
#include <cinatra/response.hpp>
#include <cinatra/logging.hpp>
#include <cinatra/aop.hpp>
#include <cinatra/body_parser.hpp>

#include <boost/lexical_cast.hpp>

#include <string>
#include <vector>

namespace cinatra
{
	template<typename... Aspect>
	class Cinatra
	{
	public:
		template<typename F>
		typename std::enable_if<(function_traits<F>::arity>0), Cinatra&>::type route(const std::string& name,const F& f)
		{
			router_.route(name, f);
			return *this;
		}

		template<typename F, typename Self>
		typename std::enable_if<(function_traits<F>::arity>0), Cinatra&>::type route(const std::string& name, const F& f, Self* self)
		{
			router_.route(name, f, self);
			return *this;
		}

		template<typename F>
		typename std::enable_if<function_traits<F>::arity==0, Cinatra&>::type route(const std::string& name,const F& f)
		{
			route(name, [f](cinatra::Request& req, cinatra::Response& res)
			{
				auto r = f();
				res.write(boost::lexical_cast<std::string>(r));
			});

			return *this;
		}

		template<typename F, typename Self>
		typename std::enable_if<function_traits<F>::arity == 0, Cinatra&>::type route(const std::string& name, const F& f, Self* self)
		{
			route(name, [f, self](cinatra::Request& req, cinatra::Response& res)
			{
				auto r = (*self.*f)();
				res.write(boost::lexical_cast<std::string>(r));
			});

			return *this;
		}

		Cinatra& threads(int num)
		{
			num_threads_ = num < 1 ? 1 : num;
			return *this;
		}

		Cinatra& error_handler(error_handler_t error_handler)
		{
			error_handler_ = error_handler;
			return *this;
		}

		Cinatra& listen(const std::string& address, const std::string& port)
		{
			listen_addr_ = address;
			listen_port_ = port;
			return *this;
		}

		Cinatra& listen(const std::string& port)
		{
			listen_addr_ = "0.0.0.0";
			listen_port_ = port;
			return *this;
		}

		Cinatra& listen(const std::string& address, unsigned short port)
		{
			listen_addr_ = address;
			listen_port_ = boost::lexical_cast<std::string>(port);
			return *this;
		}

		Cinatra& static_dir(const std::string& dir)
		{
			static_dir_ = dir;
			return *this;
		}

#ifdef CINATRA_ENABLE_HTTPS
		Cinatra& https_config(const HttpsConfig& cfg)
		{
			config_ = cfg;
			return *this;
		}
#endif // CINATRA_ENABLE_HTTPS

		void run()
		{
			HTTPServer s(num_threads_);

			s.set_request_handler([this](Request& req, Response& res)
			{
				return Invoke<sizeof...(Aspect)>(res, &Cinatra::dispatch, this, req, res);
			})
				.set_error_handler([this](int code, const std::string& msg, Request& req, Response& res)
			{
				LOG_DBG << "Handle error:" << code << " " << msg << " with path " << req.path();
				if (error_handler_
					&& error_handler_(code,msg,req,res))
				{
					return true;
				}

				LOG_DBG << "In defaule error handler";
				std::string html;
				auto s = status_header(code);
				html = "<title>" + s.second + "</title>";
				html += "";
				html += "
" + boost::lexical_cast<std::string>(s.first) + " " + s.second + " " + "
";
				if (!msg.empty())
				{
					html += "

Message: " + msg + "
";
				}
				html += "";

				res.set_status_code(s.first);

				res.write(html);

				return true;
			})
				.static_dir(static_dir_)
#ifdef CINATRA_ENABLE_HTTPS
				.https_config(config_)
#endif // CINATRA_ENABLE_HTTPS
				.listen(listen_addr_, listen_port_)
				.run();
		}

	private:
		template<size_t I, typename Func, typename Self, typename... Args>
		typename std::enable_if::type Invoke(Response& /* res */, Func&&f, Self* self, Args&&... args)
		{
			return (*self.*f)(std::forward<Args>(args)...);
		}

		template<size_t I, typename Func, typename Self, typename... Args>
		typename std::enable_if < (I > 0), bool > ::type Invoke(Response& res, Func&& /* f */, Self* /* self */, Args&&... args)
		{
			return invoke<Aspect...>(res, &Cinatra::dispatch, this, args...);
		}

		bool dispatch(Request& req, Response& res)
		{
			return router_.dispatch(req, res);
		}

	private:
#ifndef CINATRA_SINGLE_THREAD
		int num_threads_ = 0;
#endif // CINATRA_SINGLE_THREAD

		std::string listen_addr_;
		std::string listen_port_;
		std::string static_dir_;
#ifdef CINATRA_ENABLE_HTTPS
		HttpsConfig config_;
#endif // CINATRA_ENABLE_HTTPS

		error_handler_t error_handler_;

		HTTPRouter router_;
	};

	using SimpleApp = Cinatra<>;
}
<commit_msg>Update cinatra.hpp<commit_after>
#pragma once

#include <cinatra/http_server.hpp>
#include <cinatra/http_router.hpp>
#include <cinatra/request.hpp>
#include <cinatra/response.hpp>
#include <cinatra/logging.hpp>
#include <cinatra/aop.hpp>
#include <cinatra/body_parser.hpp>

#include <boost/lexical_cast.hpp>

#include <string>
#include <vector>

namespace cinatra
{
	template<typename... Aspect>
	class Cinatra
	{
	public:
		template<typename F>
		typename std::enable_if<(function_traits<F>::arity>0), Cinatra&>::type route(const std::string& name,const F& f)
		{
			router_.route(name, f);
			return *this;
		}

		template<typename F, typename Self>
		typename std::enable_if<(function_traits<F>::arity>0), Cinatra&>::type route(const std::string& name, const F& f, Self* self)
		{
			router_.route(name, f, self);
			return *this;
		}

		template<typename F>
		typename std::enable_if<function_traits<F>::arity==0, Cinatra&>::type route(const std::string& name,const F& f)
		{
			route(name, [f](cinatra::Request& req, cinatra::Response& res)
			{
				auto r = f();
				res.write(boost::lexical_cast<std::string>(r));
			});

			return *this;
		}

		template<typename F, typename Self>
		typename std::enable_if<function_traits<F>::arity == 0, Cinatra&>::type route(const std::string& name, const F& f, Self* self)
		{
			route(name, [f, self](cinatra::Request& req, cinatra::Response& res)
			{
				auto r = (*self.*f)();
				res.write(boost::lexical_cast<std::string>(r));
			});

			return *this;
		}

		Cinatra& threads(int num)
		{
			num_threads_ = num < 1 ? 1 : num;
			return *this;
		}

		Cinatra& error_handler(error_handler_t error_handler)
		{
			error_handler_ = error_handler;
			return *this;
		}

		Cinatra& listen(const std::string& address, const std::string& port)
		{
			listen_addr_ = address;
			listen_port_ = port;
			return *this;
		}

		Cinatra& listen(const std::string& port)
		{
			listen_addr_ = "0.0.0.0";
			listen_port_ = port;
			return *this;
		}

		Cinatra& listen(const std::string& address, unsigned short port)
		{
			listen_addr_ = address;
			listen_port_ = boost::lexical_cast<std::string>(port);
			return *this;
		}

		Cinatra& static_dir(const std::string& dir)
		{
			static_dir_ = dir;
			return *this;
		}

#ifdef CINATRA_ENABLE_HTTPS
		Cinatra& https_config(const HttpsConfig& cfg)
		{
			config_ = cfg;
			return *this;
		}
#endif // CINATRA_ENABLE_HTTPS

		void run()
		{
			HTTPServer s(num_threads_);

			s.set_request_handler([this](Request& req, Response& res)
			{
				return Invoke<sizeof...(Aspect)>(res, &Cinatra::dispatch, this, req, res);
			})
				.set_error_handler([this](int code, const std::string& msg, Request& req, Response& res)
			{
				LOG_DBG << "Handle error:" << code << " " << msg << " with path " << req.path();
				if (error_handler_
					&& error_handler_(code,msg,req,res))
				{
					return true;
				}

				LOG_DBG << "In defaule error handler";
				std::string html;
				auto s = status_header(code);
				html = "<title>" + s.second + "</title>";
				html += "";
				html += "
" + boost::lexical_cast<std::string>(s.first) + " " + s.second + " " + "
";
				if (!msg.empty())
				{
					html += "

Message: " + msg + "
";
				}
				html += "";

				res.set_status_code(s.first);

				res.write(html);

				return true;
			})
				.static_dir(static_dir_)
#ifdef CINATRA_ENABLE_HTTPS
				.https_config(config_)
#endif // CINATRA_ENABLE_HTTPS
				.listen(listen_addr_, listen_port_)
				.run();
		}

	private:
		template<size_t I, typename Func, typename Self, typename... Args>
		typename std::enable_if::type Invoke(Response& /* res */, Func&&f, Self* self, Args&&... args)
		{
			return (*self.*f)(std::forward<Args>(args)...);
		}

		template<size_t I, typename Func, typename Self, typename... Args>
		typename std::enable_if < (I > 0), bool > ::type Invoke(Response& res, Func&& /* f */, Self* /* self */, Args&&... args)
		{
			return invoke<Aspect...>(res, &Cinatra::dispatch, this, args...);
		}

		bool dispatch(Request& req, Response& res)
		{
			return router_.dispatch(req, res);
		}

	private:
#ifndef CINATRA_SINGLE_THREAD
	int num_threads_ = 0;
#else
	int num_threads_ = std::thread::hardware_concurrency();
#endif // CINATRA_SINGLE_THREAD

		std::string listen_addr_;
		std::string listen_port_;
		std::string static_dir_;
#ifdef CINATRA_ENABLE_HTTPS
		HttpsConfig config_;
#endif // CINATRA_ENABLE_HTTPS

		error_handler_t error_handler_;

		HTTPRouter router_;
	};

	using SimpleApp = Cinatra<>;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_FAST_VECTOR_HPP
#define ETL_FAST_VECTOR_HPP

#include <cstddef>
#include <type_traits>
#include <utility>
#include <algorithm>
#include <array>
#include <vector>
#include <numeric>

#include "assert.hpp"
#include "tmp.hpp"
#include "fast_op.hpp"
#include "fast_expr.hpp"

namespace etl {

template<typename T, std::size_t Rows>
class fast_vector {
private:
    std::array<T, Rows> _data;

public:
    static constexpr const std::size_t rows = Rows;

    typedef T value_type;
    typedef T* iterator;
    typedef const T* const_iterator;

    fast_vector(){
        //Nothing else to init
    }

    fast_vector(const T& value){
        std::fill(_data.begin(), _data.end(), value);
    }

    //Construct from expression

    fast_vector& operator=(const fast_vector& rhs){
        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = rhs[i];
        }

        return *this;
    }

    template<typename LE, typename Op, typename RE>
    fast_vector(binary_expr<T, LE, Op, RE>&& e){
        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = e[i];
        }
    }

    template<typename LE, typename Op, typename RE>
    fast_vector& operator=(binary_expr<T, LE, Op, RE>&& e){
        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = e[i];
        }

        return *this;
    }

    fast_vector& operator=(const std::vector<T>& vec){
        etl_assert(vec.size() == Rows, "Cannot copy from a vector of different size");

        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = vec[i];
        }

        return *this;
    }

    fast_vector& operator=(const vector<T>& vec){
        etl_assert(vec.size() == Rows, "Cannot copy from a vector of different size");

        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = vec[i];
        }

        return *this;
    }

    //Prohibit copy
    fast_vector(const fast_vector& rhs) = delete;

    //Allow move
    fast_vector(fast_vector&& rhs) = default;
    fast_vector& operator=(fast_vector&& rhs) = default;

    //Modifiers

    //Set every element to the same scalar
    void operator=(const T& value){
        std::fill(_data.begin(), _data.end(), value);
    }

    //Multiply each element by a scalar
    fast_vector& operator*=(const T& value){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] *= value;
        }

        return *this;
    }

    //Divide each element by a scalar
    fast_vector& operator/=(const T& value){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] /= value;
        }

        return *this;
    }

    template<typename RE>
    fast_vector& operator+=(RE&& rhs){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] += rhs[i];
        }

        return *this;
    }

    template<typename RE>
    fast_vector& operator-=(RE&& rhs){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] -= rhs[i];
        }

        return *this;
    }

    //Add a scalar to each element
    template<typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator+(RE re) const -> binary_expr<T, const fast_vector&, plus_binary_op<T>, scalar<T>> {
        return {*this, re};
    }

    //Add elements of vector together
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator+(RE&& re) const -> binary_expr<T, const fast_vector&, plus_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Remove each element by a scalar
    template<typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator-(RE re) const -> binary_expr<T, const fast_vector&, minus_binary_op<T>, scalar<T>> {
        return {*this, re};
    }

    //Sub elements of vector together
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator-(RE&& re) const -> binary_expr<T, const fast_vector&, minus_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Mul elements of vector togethers
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator*(RE&& re) const -> binary_expr<T, const fast_vector&, mul_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Div elements of vector togethers
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator/(RE&& re) const -> binary_expr<T, const fast_vector&, div_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Accessors

    constexpr size_t size() const {
        return rows;
    }

    T& operator()(size_t i){
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    const T& operator()(size_t i) const {
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    T& operator[](size_t i){
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    const T& operator[](size_t i) const {
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    const_iterator begin() const {
        return _data.begin();
    }

    iterator begin(){
        return _data.begin();
    }

    const_iterator end() const {
        return _data.end();
    }

    iterator end(){
        return _data.end();
    }
};

template<typename T, std::size_t Rows, typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
auto operator*(const fast_vector<T, Rows>& lhs, RE rhs) -> binary_expr<T, const fast_vector<T, Rows>&, mul_binary_op<T>, scalar<T>> {
    return {lhs, rhs};
}

template<typename T, std::size_t Rows, typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
auto operator*(RE lhs, const fast_vector<T, Rows>& rhs) -> binary_expr<T, scalar<T>, mul_binary_op<T>, const fast_vector<T, Rows>&> {
    return {lhs, rhs};
}

template<typename T, std::size_t Rows, typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
auto operator/(const fast_vector<T, Rows>& lhs, RE rhs) -> binary_expr<T, const fast_vector<T, Rows>&, div_binary_op<T>, scalar<T>> {
    return {lhs, rhs};
}

template<typename T, std::size_t Rows>
auto abs(const fast_vector<T, Rows>& value) -> unary_expr<T, const fast_vector<T, Rows>&, abs_unary_op<T>> {
    return {value};
}

template<typename T, std::size_t Rows>
auto sign(const fast_vector<T, Rows>& value) -> unary_expr<T, const fast_vector<T, Rows>&, sign_unary_op<T>> {
    return {value};
}

template<typename T, std::size_t Rows>
T sum(const fast_vector<T, Rows>& values){
    return std::accumulate(values.begin(), values.end(), static_cast<T>(0));
}

} //end of namespace etl

#endif
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef ETL_FAST_VECTOR_HPP
#define ETL_FAST_VECTOR_HPP

#include <cstddef>
#include <type_traits>
#include <utility>
#include <algorithm>
#include <array>
#include <vector>
#include <numeric>

#include "assert.hpp"
#include "tmp.hpp"
#include "fast_op.hpp"
#include "fast_expr.hpp"

namespace etl {

template<typename T, std::size_t Rows>
class fast_vector {
private:
    std::array<T, Rows> _data;

public:
    static constexpr const std::size_t rows = Rows;

    typedef T value_type;
    typedef T* iterator;
    typedef const T* const_iterator;

    fast_vector(){
        //Nothing else to init
    }

    fast_vector(const T& value){
        std::fill(_data.begin(), _data.end(), value);
    }

    //Copy assignment operator

    fast_vector& operator=(const fast_vector& rhs){
        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = rhs[i];
        }

        return *this;
    }

    //Construct from expression

    template<typename LE, typename Op, typename RE>
    fast_vector(binary_expr<T, LE, Op, RE>&& e){
        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = e[i];
        }
    }

    template<typename LE, typename Op, typename RE>
    fast_vector& operator=(binary_expr<T, LE, Op, RE>&& e){
        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = e[i];
        }

        return *this;
    }

    //Allow copy from other containers

    template<typename Container, enable_if_u<std::is_same<Container::value_type, T>::value> = detail::dummy>
    fast_vector& operator=(const Container& vec){
        etl_assert(vec.size() == Rows, "Cannot copy from a vector of different size");

        for(std::size_t i = 0; i < Rows; ++i){
            _data[i] = vec[i];
        }

        return *this;
    }

    //Prohibit copy
    fast_vector(const fast_vector& rhs) = delete;

    //Allow move
    fast_vector(fast_vector&& rhs) = default;
    fast_vector& operator=(fast_vector&& rhs) = default;

    //Modifiers

    //Set every element to the same scalar
    void operator=(const T& value){
        std::fill(_data.begin(), _data.end(), value);
    }

    //Multiply each element by a scalar
    fast_vector& operator*=(const T& value){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] *= value;
        }

        return *this;
    }

    //Divide each element by a scalar
    fast_vector& operator/=(const T& value){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] /= value;
        }

        return *this;
    }

    template<typename RE>
    fast_vector& operator+=(RE&& rhs){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] += rhs[i];
        }

        return *this;
    }

    template<typename RE>
    fast_vector& operator-=(RE&& rhs){
        for(size_t i = 0; i < Rows; ++i){
            _data[i] -= rhs[i];
        }

        return *this;
    }

    //Add a scalar to each element
    template<typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator+(RE re) const -> binary_expr<T, const fast_vector&, plus_binary_op<T>, scalar<T>> {
        return {*this, re};
    }

    //Add elements of vector together
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator+(RE&& re) const -> binary_expr<T, const fast_vector&, plus_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Remove each element by a scalar
    template<typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator-(RE re) const -> binary_expr<T, const fast_vector&, minus_binary_op<T>, scalar<T>> {
        return {*this, re};
    }

    //Sub elements of vector together
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator-(RE&& re) const -> binary_expr<T, const fast_vector&, minus_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Mul elements of vector togethers
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator*(RE&& re) const -> binary_expr<T, const fast_vector&, mul_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Div elements of vector togethers
    template<typename RE, disable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
    auto operator/(RE&& re) const -> binary_expr<T, const fast_vector&, div_binary_op<T>, decltype(std::forward<RE>(re))> {
        return {*this, std::forward<RE>(re)};
    }

    //Accessors

    constexpr size_t size() const {
        return rows;
    }

    T& operator()(size_t i){
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    const T& operator()(size_t i) const {
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    T& operator[](size_t i){
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    const T& operator[](size_t i) const {
        etl_assert(i < rows, "Out of bounds");

        return _data[i];
    }

    const_iterator begin() const {
        return _data.begin();
    }

    iterator begin(){
        return _data.begin();
    }

    const_iterator end() const {
        return _data.end();
    }

    iterator end(){
        return _data.end();
    }
};

template<typename T, std::size_t Rows, typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
auto operator*(const fast_vector<T, Rows>& lhs, RE rhs) -> binary_expr<T, const fast_vector<T, Rows>&, mul_binary_op<T>, scalar<T>> {
    return {lhs, rhs};
}

template<typename T, std::size_t Rows, typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
auto operator*(RE lhs, const fast_vector<T, Rows>& rhs) -> binary_expr<T, scalar<T>, mul_binary_op<T>, const fast_vector<T, Rows>&> {
    return {lhs, rhs};
}

template<typename T, std::size_t Rows, typename RE, enable_if_u<std::is_convertible<RE, T>::value> = detail::dummy>
auto operator/(const fast_vector<T, Rows>& lhs, RE rhs) -> binary_expr<T, const fast_vector<T, Rows>&, div_binary_op<T>, scalar<T>> {
    return {lhs, rhs};
}

template<typename T, std::size_t Rows>
auto abs(const fast_vector<T, Rows>& value) -> unary_expr<T, const fast_vector<T, Rows>&, abs_unary_op<T>> {
    return {value};
}

template<typename T, std::size_t Rows>
auto sign(const fast_vector<T, Rows>& value) -> unary_expr<T, const fast_vector<T, Rows>&, sign_unary_op<T>> {
    return {value};
}

template<typename T, std::size_t Rows>
T sum(const fast_vector<T, Rows>& values){
    return std::accumulate(values.begin(), values.end(), static_cast<T>(0));
}

} //end of namespace etl

#endif
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2017 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#ifdef ETL_CUDA
#include "etl/impl/cublas/cuda.hpp"
#endif

namespace etl {

#ifdef ETL_CUDA

/*!
 * \brief GPU memory allocator.
 *
 * All GPU memory allocations should be made through this helper.
 */
struct gpu_memory_allocator {
private:
    /*!
     * \brief Allocate GPU memory of the given size
     * \param size The memory  size
     * \return The allocated GPU memory
     */
    template<typename T>
    static T* base_allocate(size_t size){
        T* memory = nullptr;

        auto cuda_status = cudaMalloc(&memory, size * sizeof(T));

        if (cuda_status != cudaSuccess) {
            std::cout << "cuda: Failed to allocate GPU memory: " << cudaGetErrorString(cuda_status) << std::endl;
            std::cout << "      Tried to allocate " << size * sizeof(T) << "B" << std::endl;
            exit(EXIT_FAILURE);
        }

        inc_counter("gpu:allocate");

        return memory;
    }

    /*!
     * \brief Release memory allocated by this allocator
     * \param gpu_memory The GPU memory allocated
     * \param size The size of the allocated GPU memory
     */
    static void base_release(const void* gpu_memory){
        //Note: the const_cast is only here to allow compilation
        cuda_check(cudaFree((const_cast<void*>(gpu_memory))));

        inc_counter("gpu:release");
    }

#ifndef ETL_GPU_POOL
public:
    /*!
     * \brief Allocate GPU memory of the given size
     * \param size The memory  size
     * \return The allocated GPU memory
     */
    template<typename T>
    static T* allocate(size_t size){
        return base_allocate<T>(size);
    }

    /*!
     * \brief Release memory allocated by this allocator
     * \param gpu_memory The GPU memory allocated
     * \param size The size of the allocated GPU memory
     */
    static void release(const void* gpu_memory, size_t size){
        base_release(gpu_memory, size);

        cpp_unused(size);
    }

#else // ETL_GPU_POOL

#ifdef ETL_GPU_POOL_SIZE
    static constexpr size_t limit = ETL_GPU_POOL_SIZE; ///< The limit of the pool
#else
    static constexpr size_t limit = 32; ///< The limit of the pool
#endif

    /*!
     * \brief An entry in the mini-pool
     */
    struct mini_pool_entry {
        size_t size;  ///< The size, in bytes, of the memory
        void* memory; ///< Pointer to the memory, if any
    };

    /*!
     * \brief A very simple implementation for a GPU memory pool of
     * fixed-size
     *
     * Although it's quite simple, it should help in most cases
     * where the same operations are done several times, as in
     * Machine Learning. In that case, even a very simple pool like
     * this may greatly reduce the overhead of memory allocation.
     */
    struct mini_pool {
        std::array<mini_pool_entry, limit> cache; ///< The cache of GPU memory addresses

        /*!
         * \brief Construct the mini pool
         */
        mini_pool(){
            for(auto& slot : cache){
                slot.size         = 0;
                slot.memory       = nullptr;
            }
        }

        /*!
         * \brief Destructs the GPU pool, releasing all remaining memory
         */
        ~mini_pool(){
            for(auto& slot : cache){
                if(slot.memory){
                    base_release(slot.memory);
                }
            }
        }
    };

    /*!
     * \brief Return a reference to the GPU pool
     * \return a reference to the GPU pool
     */
    static mini_pool& get_pool(){
        static mini_pool pool;
        return pool;
    }

    /*!
     * \brief Return the lock for the pool
     * \return a reference to the pool lock
     */
    static std::mutex& get_lock(){
        static std::mutex lock;
        return lock;
    }

public:
    /*!
     * \brief Allocate GPU memory of the given size
     * \param size The memory  size
     * \return The allocated GPU memory
     */
    template<typename T>
    static T* allocate(size_t size){
        const auto real_size = size * sizeof(T);

        // Try to get memory from the pool

        {
            std::lock_guard<std::mutex> l(get_lock());

            for(auto& slot : get_pool().cache){
                if(slot.memory && slot.size == real_size){
                    auto memory = slot.memory;
                    slot.memory = nullptr;
                    return static_cast<T*>(memory);
                }
            }
        }

        // If a memory block is not found, allocate new memory

        return base_allocate<T>(size);
    }

    /*!
     * \brief Release memory allocated by this allocator
     * \param gpu_memory The GPU memory allocated
     * \param size The size of the allocated GPU memory
     */
    template<typename T>
    static void release(const T* gpu_memory, size_t size){
        // Try to get an empty slot

        {
            std::lock_guard<std::mutex> l(get_lock());

            for(auto& slot : get_pool().cache){
                if(!slot.memory){
                    slot.memory = const_cast<void*>(static_cast<const void*>(gpu_memory));
                    slot.size   = size * sizeof(T);

                    return;
                }
            }
        }

        // If the cache is pool, release the memory

        base_release(gpu_memory);
    }
#endif
};

/*!
 * \brief GPU memory handler.
 *
 * This handler is responsible for allocating the memory and keeping CPU and GPU
 * memory consistency.
 */
template<typename T>
struct gpu_memory_handler {
private:
    mutable T* gpu_memory_         = nullptr; ///< The GPU memory pointer
    mutable size_t gpu_memory_size = 0;       ///< The GPU memory size

    mutable bool cpu_up_to_date = true;  ///< Is the CPU memory up to date
    mutable bool gpu_up_to_date = false; ///< Is the GPU memory up to date

public:
    gpu_memory_handler() = default;

    /*!
     * \brief Destroys the GPU memory handler. This effectively
     * releases any memory allocated.
     */
    ~gpu_memory_handler(){
        if (gpu_memory_) {
            gpu_memory_allocator::release(gpu_memory_, gpu_memory_size);
        }
    }

    /*!
     * \brief Indicates if the CPU memory is up to date.
     * \return true if the CPU memory is up to date, false otherwise.
     */
    bool is_cpu_up_to_date() const noexcept {
        return cpu_up_to_date;
    }

    /*!
     * \brief Indicates if the GPU memory is up to date.
     * \return true if the GPU memory is up to date, false otherwise.
     */
    bool is_gpu_up_to_date() const noexcept {
        return gpu_up_to_date;
    }

    /*!
     * \brief Return GPU memory of this expression, if any.
     * \return a pointer to the GPU memory or nullptr if not allocated in GPU.
     */
    T* gpu_memory() const noexcept {
        return gpu_memory_;
    }

    /*!
     * \brief Evict the expression from GPU.
     */
    void gpu_evict() const noexcept {
        if (gpu_memory_) {
            gpu_memory_allocator::release(gpu_memory_, gpu_memory_size);

            gpu_memory_ = nullptr;
            gpu_memory_size = 0;
        }

        invalidate_gpu();
    }

    /*!
     * \brief Invalidates the CPU memory
     */
    void invalidate_cpu() const noexcept {
        cpu_up_to_date = false;
    }

    /*!
     * \brief Invalidates the GPU memory
     */
    void invalidate_gpu() const noexcept {
        gpu_up_to_date = false;
    }

    /*!
     * \brief Validates the CPU memory
     */
    void validate_cpu() const noexcept {
        cpu_up_to_date = true;
    }

    /*!
     * \brief Validates the GPU memory
     */
    void validate_gpu() const noexcept {
        gpu_up_to_date = true;
    }

    /*!
     * \brief Ensures that the GPU memory is allocated and that the GPU memory
     * is up to date (to undefined value).
     * \param etl_size The size of the memory
     */
    void ensure_gpu_allocated(size_t etl_size) const {
        if (!is_gpu_allocated()) {
            gpu_allocate_impl(etl_size);
        }
    }

    /*!
     * \brief Allocate memory on the GPU for the expression and copy the values into the GPU.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_gpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        // Make sure there is some memory allocate
        if (!is_gpu_allocated()) {
            gpu_allocate_impl(etl_size);
        }

        if(!gpu_up_to_date){
            cpu_to_gpu(cpu_memory, etl_size);
        }
    }

    /*!
     * \brief Copy back from the GPU to the expression memory if
     * necessary.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_cpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        if(!cpu_up_to_date){
            gpu_to_cpu(cpu_memory, etl_size);
        }
    }

    /*!
     * \brief Copy from GPU to GPU
     * \param gpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void gpu_copy_from(const T* gpu_memory, size_t etl_size) const {
        cpp_assert(is_gpu_allocated(), "GPU must be allocated before copy");

        cuda_check(cudaMemcpy(
            const_cast<std::remove_const_t<T>*>(gpu_memory_),
            const_cast<std::remove_const_t<T>*>(gpu_memory),
            etl_size * sizeof(T), cudaMemcpyDeviceToDevice));

        gpu_up_to_date = true;
        cpu_up_to_date = false;
    }

private:

    /*!
     * \brief Allocate memory on the GPU for the expression
     */
    void gpu_allocate_impl(size_t etl_size) const {
        cpp_assert(!is_gpu_allocated(), "Trying to allocate already allocated GPU gpu_memory_");

        gpu_memory_     = gpu_memory_allocator::allocate<T>(etl_size);
        gpu_memory_size = etl_size;
    }

    /*!
     * \brief Copy back from the CPU to the GPU
     */
    void cpu_to_gpu(const T* cpu_memory, size_t etl_size) const {
        cpp_assert(is_gpu_allocated(), "Cannot copy to unallocated GPU memory");
        cpp_assert(!gpu_up_to_date, "Copy must only be done if necessary");
        cpp_assert(cpu_up_to_date, "Copy from invalid memory!");

        cuda_check(cudaMemcpy(
            const_cast<std::remove_const_t<T>*>(gpu_memory_),
            const_cast<std::remove_const_t<T>*>(cpu_memory),
            etl_size * sizeof(T), cudaMemcpyHostToDevice));

        gpu_up_to_date = true;

        inc_counter("gpu:cpu_to_gpu");
    }

    /*!
     * \brief Copy back from the GPU to the expression memory.
     */
    void gpu_to_cpu(const T* cpu_memory, size_t etl_size) const {
        cpp_assert(is_gpu_allocated(), "Cannot copy from unallocated GPU memory()");
        cpp_assert(gpu_up_to_date, "Cannot copy from invalid memory");
        cpp_assert(!cpu_up_to_date, "Copy done without reason");

        cuda_check(cudaMemcpy(
            const_cast<std::remove_const_t<T>*>(cpu_memory),
            const_cast<std::remove_const_t<T>*>(gpu_memory_),
            etl_size * sizeof(T), cudaMemcpyDeviceToHost));

        cpu_up_to_date = true;

        inc_counter("gpu:gpu_to_cpu");
    }

    /*!
     * \brief Indicates if the expression is allocated in GPU.
     * \return true if the expression is allocated in GPU, false otherwise
     */
    bool is_gpu_allocated() const noexcept {
        return gpu_memory_;
    }
};

#else
template<typename T>
struct gpu_memory_handler {
    /*!
     * \brief Return GPU memory of this expression, if any.
     * \return a pointer to the GPU memory or nullptr if not allocated in GPU.
     */
    T* gpu_memory() const noexcept {
        return nullptr;
    }

    /*!
     * \brief Indicates if the CPU memory is up to date.
     * \return true if the CPU memory is up to date, false otherwise.
     */
    bool is_cpu_up_to_date() const noexcept {
        return false;
    }

    /*!
     * \brief Indicates if the GPU memory is up to date.
     * \return true if the GPU memory is up to date, false otherwise.
     */
    bool is_gpu_up_to_date() const noexcept {
        return false;
    }

    /*!
     * \brief Evict the expression from GPU.
     */
    void gpu_evict() const noexcept {}

    /*!
     * \brief Invalidates the CPU memory
     */
    void invalidate_cpu() const noexcept {}

    /*!
     * \brief Invalidates the GPU memory
     */
    void invalidate_gpu() const noexcept {}

    /*!
     * \brief Validates the CPU memory
     */
    void validate_cpu() const noexcept {}

    /*!
     * \brief Validates the GPU memory
     */
    void validate_gpu() const noexcept {}

    /*!
     * \brief Ensures that the GPU memory is allocated and that the GPU memory
     * is up to date (to undefined value).
     * \param etl_size The size of the memory
     */
    void ensure_gpu_allocated(size_t etl_size) const {
        cpp_unused(etl_size);
    }

    /*!
     * \brief Allocate memory on the GPU for the expression and copy the values into the GPU.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_gpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        cpp_unused(cpu_memory);
        cpp_unused(etl_size);
    }

    /*!
     * \brief Copy back from the GPU to the expression memory if
     * necessary.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_cpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        cpp_unused(cpu_memory);
        cpp_unused(etl_size);
    }

    /*!
     * \brief Copy from GPU to GPU
     * \param gpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void gpu_copy_from(const T* gpu_memory, size_t etl_size) const {
        cpp_unused(gpu_memory);
        cpp_unused(etl_size);
    }
};
#endif

} //end of namespace etl

#ifdef ETL_CUDA
#include "etl/impl/cublas/cuda_memory.hpp"
#endif
<commit_msg>Add support to clear the GPU pool<commit_after>//=======================================================================
// Copyright (c) 2014-2017 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#ifdef ETL_CUDA
#include "etl/impl/cublas/cuda.hpp"
#endif

namespace etl {

#ifdef ETL_CUDA

/*!
 * \brief GPU memory allocator.
 *
 * All GPU memory allocations should be made through this helper.
 */
struct gpu_memory_allocator {
private:
    /*!
     * \brief Allocate GPU memory of the given size
     * \param size The memory  size
     * \return The allocated GPU memory
     */
    template<typename T>
    static T* base_allocate(size_t size){
        T* memory = nullptr;

        auto cuda_status = cudaMalloc(&memory, size * sizeof(T));

        if (cuda_status != cudaSuccess) {
            std::cout << "cuda: Failed to allocate GPU memory: " << cudaGetErrorString(cuda_status) << std::endl;
            std::cout << "      Tried to allocate " << size * sizeof(T) << "B" << std::endl;
            exit(EXIT_FAILURE);
        }

        inc_counter("gpu:allocate");

        return memory;
    }

    /*!
     * \brief Release memory allocated by this allocator
     * \param gpu_memory The GPU memory allocated
     * \param size The size of the allocated GPU memory
     */
    static void base_release(const void* gpu_memory){
        //Note: the const_cast is only here to allow compilation
        cuda_check(cudaFree((const_cast<void*>(gpu_memory))));

        inc_counter("gpu:release");
    }

#ifndef ETL_GPU_POOL
public:
    /*!
     * \brief Allocate GPU memory of the given size
     * \param size The memory  size
     * \return The allocated GPU memory
     */
    template<typename T>
    static T* allocate(size_t size){
        return base_allocate<T>(size);
    }

    /*!
     * \brief Release memory allocated by this allocator
     * \param gpu_memory The GPU memory allocated
     * \param size The size of the allocated GPU memory
     */
    static void release(const void* gpu_memory, size_t size){
        base_release(gpu_memory, size);

        cpp_unused(size);
    }

    /*!
     * \brief Release all memory acquired by the allocator.
     *
     * This has no effect if the allocator does not use a memory
     * pool.
     */
    static void clear(){
        // This allocator does not store memory
    }

#else // ETL_GPU_POOL

#ifdef ETL_GPU_POOL_SIZE
    static constexpr size_t limit = ETL_GPU_POOL_SIZE; ///< The limit of the pool
#else
    static constexpr size_t limit = 32; ///< The limit of the pool
#endif

    /*!
     * \brief An entry in the mini-pool
     */
    struct mini_pool_entry {
        size_t size;  ///< The size, in bytes, of the memory
        void* memory; ///< Pointer to the memory, if any
    };

    /*!
     * \brief A very simple implementation for a GPU memory pool of
     * fixed-size
     *
     * Although it's quite simple, it should help in most cases
     * where the same operations are done several times, as in
     * Machine Learning. In that case, even a very simple pool like
     * this may greatly reduce the overhead of memory allocation.
     */
    struct mini_pool {
        std::array<mini_pool_entry, limit> cache; ///< The cache of GPU memory addresses

        /*!
         * \brief Construct the mini pool
         */
        mini_pool(){
            for(auto& slot : cache){
                slot.size         = 0;
                slot.memory       = nullptr;
            }
        }

        /*!
         * \brief Destructs the GPU pool, releasing all remaining memory
         */
        ~mini_pool(){
            for(auto& slot : cache){
                if(slot.memory){
                    base_release(slot.memory);
                }
            }
        }
    };

    /*!
     * \brief Return a reference to the GPU pool
     * \return a reference to the GPU pool
     */
    static mini_pool& get_pool(){
        static mini_pool pool;
        return pool;
    }

    /*!
     * \brief Return the lock for the pool
     * \return a reference to the pool lock
     */
    static std::mutex& get_lock(){
        static std::mutex lock;
        return lock;
    }

public:
    /*!
     * \brief Allocate GPU memory of the given size
     * \param size The memory  size
     * \return The allocated GPU memory
     */
    template<typename T>
    static T* allocate(size_t size){
        const auto real_size = size * sizeof(T);

        // Try to get memory from the pool

        {
            std::lock_guard<std::mutex> l(get_lock());

            for(auto& slot : get_pool().cache){
                if(slot.memory && slot.size == real_size){
                    auto memory = slot.memory;
                    slot.memory = nullptr;
                    return static_cast<T*>(memory);
                }
            }
        }

        // If a memory block is not found, allocate new memory

        return base_allocate<T>(size);
    }

    /*!
     * \brief Release memory allocated by this allocator
     * \param gpu_memory The GPU memory allocated
     * \param size The size of the allocated GPU memory
     */
    template<typename T>
    static void release(const T* gpu_memory, size_t size){
        // Try to get an empty slot

        {
            std::lock_guard<std::mutex> l(get_lock());

            for(auto& slot : get_pool().cache){
                if(!slot.memory){
                    slot.memory = const_cast<void*>(static_cast<const void*>(gpu_memory));
                    slot.size   = size * sizeof(T);

                    return;
                }
            }
        }

        // If the cache is pool, release the memory

        base_release(gpu_memory);
    }

    /*!
     * \brief Release all memory acquired by the allocator.
     *
     * This has no effect if the allocator does not use a memory
     * pool.
     */
    static void clear(){
        std::lock_guard<std::mutex> l(get_lock());

        // Release each used slots
        // and clear them

        for(auto& slot : get_pool().cache){
            if(slot.memory){
                base_release(slot.memory);

                slot.memory = nullptr;
                slot.size   = 0;
            }
        }
    }
#endif
};

/*!
 * \brief GPU memory handler.
 *
 * This handler is responsible for allocating the memory and keeping CPU and GPU
 * memory consistency.
 */
template<typename T>
struct gpu_memory_handler {
private:
    mutable T* gpu_memory_         = nullptr; ///< The GPU memory pointer
    mutable size_t gpu_memory_size = 0;       ///< The GPU memory size

    mutable bool cpu_up_to_date = true;  ///< Is the CPU memory up to date
    mutable bool gpu_up_to_date = false; ///< Is the GPU memory up to date

public:
    gpu_memory_handler() = default;

    /*!
     * \brief Destroys the GPU memory handler. This effectively
     * releases any memory allocated.
     */
    ~gpu_memory_handler(){
        if (gpu_memory_) {
            gpu_memory_allocator::release(gpu_memory_, gpu_memory_size);
        }
    }

    /*!
     * \brief Indicates if the CPU memory is up to date.
     * \return true if the CPU memory is up to date, false otherwise.
     */
    bool is_cpu_up_to_date() const noexcept {
        return cpu_up_to_date;
    }

    /*!
     * \brief Indicates if the GPU memory is up to date.
     * \return true if the GPU memory is up to date, false otherwise.
     */
    bool is_gpu_up_to_date() const noexcept {
        return gpu_up_to_date;
    }

    /*!
     * \brief Return GPU memory of this expression, if any.
     * \return a pointer to the GPU memory or nullptr if not allocated in GPU.
     */
    T* gpu_memory() const noexcept {
        return gpu_memory_;
    }

    /*!
     * \brief Evict the expression from GPU.
     */
    void gpu_evict() const noexcept {
        if (gpu_memory_) {
            gpu_memory_allocator::release(gpu_memory_, gpu_memory_size);

            gpu_memory_ = nullptr;
            gpu_memory_size = 0;
        }

        invalidate_gpu();
    }

    /*!
     * \brief Invalidates the CPU memory
     */
    void invalidate_cpu() const noexcept {
        cpu_up_to_date = false;
    }

    /*!
     * \brief Invalidates the GPU memory
     */
    void invalidate_gpu() const noexcept {
        gpu_up_to_date = false;
    }

    /*!
     * \brief Validates the CPU memory
     */
    void validate_cpu() const noexcept {
        cpu_up_to_date = true;
    }

    /*!
     * \brief Validates the GPU memory
     */
    void validate_gpu() const noexcept {
        gpu_up_to_date = true;
    }

    /*!
     * \brief Ensures that the GPU memory is allocated and that the GPU memory
     * is up to date (to undefined value).
     * \param etl_size The size of the memory
     */
    void ensure_gpu_allocated(size_t etl_size) const {
        if (!is_gpu_allocated()) {
            gpu_allocate_impl(etl_size);
        }
    }

    /*!
     * \brief Allocate memory on the GPU for the expression and copy the values into the GPU.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_gpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        // Make sure there is some memory allocate
        if (!is_gpu_allocated()) {
            gpu_allocate_impl(etl_size);
        }

        if(!gpu_up_to_date){
            cpu_to_gpu(cpu_memory, etl_size);
        }
    }

    /*!
     * \brief Copy back from the GPU to the expression memory if
     * necessary.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_cpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        if(!cpu_up_to_date){
            gpu_to_cpu(cpu_memory, etl_size);
        }
    }

    /*!
     * \brief Copy from GPU to GPU
     * \param gpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void gpu_copy_from(const T* gpu_memory, size_t etl_size) const {
        cpp_assert(is_gpu_allocated(), "GPU must be allocated before copy");

        cuda_check(cudaMemcpy(
            const_cast<std::remove_const_t<T>*>(gpu_memory_),
            const_cast<std::remove_const_t<T>*>(gpu_memory),
            etl_size * sizeof(T), cudaMemcpyDeviceToDevice));

        gpu_up_to_date = true;
        cpu_up_to_date = false;
    }

private:

    /*!
     * \brief Allocate memory on the GPU for the expression
     */
    void gpu_allocate_impl(size_t etl_size) const {
        cpp_assert(!is_gpu_allocated(), "Trying to allocate already allocated GPU gpu_memory_");

        gpu_memory_     = gpu_memory_allocator::allocate<T>(etl_size);
        gpu_memory_size = etl_size;
    }

    /*!
     * \brief Copy back from the CPU to the GPU
     */
    void cpu_to_gpu(const T* cpu_memory, size_t etl_size) const {
        cpp_assert(is_gpu_allocated(), "Cannot copy to unallocated GPU memory");
        cpp_assert(!gpu_up_to_date, "Copy must only be done if necessary");
        cpp_assert(cpu_up_to_date, "Copy from invalid memory!");

        cuda_check(cudaMemcpy(
            const_cast<std::remove_const_t<T>*>(gpu_memory_),
            const_cast<std::remove_const_t<T>*>(cpu_memory),
            etl_size * sizeof(T), cudaMemcpyHostToDevice));

        gpu_up_to_date = true;

        inc_counter("gpu:cpu_to_gpu");
    }

    /*!
     * \brief Copy back from the GPU to the expression memory.
     */
    void gpu_to_cpu(const T* cpu_memory, size_t etl_size) const {
        cpp_assert(is_gpu_allocated(), "Cannot copy from unallocated GPU memory()");
        cpp_assert(gpu_up_to_date, "Cannot copy from invalid memory");
        cpp_assert(!cpu_up_to_date, "Copy done without reason");

        cuda_check(cudaMemcpy(
            const_cast<std::remove_const_t<T>*>(cpu_memory),
            const_cast<std::remove_const_t<T>*>(gpu_memory_),
            etl_size * sizeof(T), cudaMemcpyDeviceToHost));

        cpu_up_to_date = true;

        inc_counter("gpu:gpu_to_cpu");
    }

    /*!
     * \brief Indicates if the expression is allocated in GPU.
     * \return true if the expression is allocated in GPU, false otherwise
     */
    bool is_gpu_allocated() const noexcept {
        return gpu_memory_;
    }
};

#else
template<typename T>
struct gpu_memory_handler {
    /*!
     * \brief Return GPU memory of this expression, if any.
     * \return a pointer to the GPU memory or nullptr if not allocated in GPU.
     */
    T* gpu_memory() const noexcept {
        return nullptr;
    }

    /*!
     * \brief Indicates if the CPU memory is up to date.
     * \return true if the CPU memory is up to date, false otherwise.
     */
    bool is_cpu_up_to_date() const noexcept {
        return false;
    }

    /*!
     * \brief Indicates if the GPU memory is up to date.
     * \return true if the GPU memory is up to date, false otherwise.
     */
    bool is_gpu_up_to_date() const noexcept {
        return false;
    }

    /*!
     * \brief Evict the expression from GPU.
     */
    void gpu_evict() const noexcept {}

    /*!
     * \brief Invalidates the CPU memory
     */
    void invalidate_cpu() const noexcept {}

    /*!
     * \brief Invalidates the GPU memory
     */
    void invalidate_gpu() const noexcept {}

    /*!
     * \brief Validates the CPU memory
     */
    void validate_cpu() const noexcept {}

    /*!
     * \brief Validates the GPU memory
     */
    void validate_gpu() const noexcept {}

    /*!
     * \brief Ensures that the GPU memory is allocated and that the GPU memory
     * is up to date (to undefined value).
     * \param etl_size The size of the memory
     */
    void ensure_gpu_allocated(size_t etl_size) const {
        cpp_unused(etl_size);
    }

    /*!
     * \brief Allocate memory on the GPU for the expression and copy the values into the GPU.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_gpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        cpp_unused(cpu_memory);
        cpp_unused(etl_size);
    }

    /*!
     * \brief Copy back from the GPU to the expression memory if
     * necessary.
     * \param cpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void ensure_cpu_up_to_date(const T* cpu_memory, size_t etl_size) const {
        cpp_unused(cpu_memory);
        cpp_unused(etl_size);
    }

    /*!
     * \brief Copy from GPU to GPU
     * \param gpu_memory Pointer to CPU memory
     * \param etl_size The size of the memory
     */
    void gpu_copy_from(const T* gpu_memory, size_t etl_size) const {
        cpp_unused(gpu_memory);
        cpp_unused(etl_size);
    }
};
#endif

} //end of namespace etl

#ifdef ETL_CUDA
#include "etl/impl/cublas/cuda_memory.hpp"
#endif
<|endoftext|>