Merge pull request #99 from an-tao/custom_ctrl

Provides a way for users to create and initialize controllers
This commit is contained in:
An Tao 2019-03-30 23:46:10 +08:00 committed by GitHub
commit d0b9ed84ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 357 additions and 90 deletions

View File

@ -0,0 +1,11 @@
#include "CustomCtrl.h"
//add definition of your processing function here
void CustomCtrl::hello(const HttpRequestPtr &req,
const std::function<void(const HttpResponsePtr &)> &callback,
const std::string &userName) const
{
auto resp = HttpResponse::newHttpResponse();
resp->setBody("<P>" + _greetings + ", " + userName + "</P>");
callback(resp);
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <drogon/HttpController.h>
using namespace drogon;
class CustomCtrl : public drogon::HttpController<CustomCtrl, false>
{
public:
METHOD_LIST_BEGIN
//use METHOD_ADD to add your custom processing function here;
METHOD_ADD(CustomCtrl::hello, "/{1}", Get, "CustomHeaderFilter"); //path is /customctrl/{arg1}
METHOD_LIST_END
explicit CustomCtrl(const std::string &greetings) : _greetings(greetings) {}
void hello(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback, const std::string &userName) const;
private:
std::string _greetings;
};

View File

@ -0,0 +1,25 @@
/**
*
* CustomHeaderFilter.cc
*
*/
#include "CustomHeaderFilter.h"
using namespace drogon;
void CustomHeaderFilter::doFilter(const HttpRequestPtr &req,
const FilterCallback &fcb,
const FilterChainCallback &fccb)
{
if (req->getHeader(_field) == _value)
{
//Passed
fccb();
return;
}
//Check failed
auto res = drogon::HttpResponse::newHttpResponse();
res->setStatusCode(k500InternalServerError);
fcb(res);
}

View File

@ -0,0 +1,25 @@
/**
*
* CustomHeaderFilter.h
*
*/
#pragma once
#include <drogon/HttpFilter.h>
using namespace drogon;
class CustomHeaderFilter : public HttpFilter<CustomHeaderFilter, false>
{
public:
CustomHeaderFilter(const std::string &field, const std::string &value)
: _field(field),
_value(value) {}
virtual void doFilter(const HttpRequestPtr &req,
const FilterCallback &fcb,
const FilterChainCallback &fccb) override;
private:
std::string _field;
std::string _value;
};

View File

@ -2,6 +2,8 @@
#include <drogon/drogon.h>
#include <vector>
#include <string>
#include "CustomCtrl.h"
#include "CustomHeaderFilter.h"
using namespace drogon;
class A : public DrObjectBase
@ -141,9 +143,15 @@ int main()
//drogon::HttpAppFramework::instance().enableDynamicViewsLoading({"/tmp/views"});
app().loadConfigFile("config.example.json");
auto &json = app().getCustomConfig();
if(json.empty())
if (json.empty())
{
std::cout << "empty custom config!" << std::endl;
}
//Install custom controller
auto ctrlPtr = std::make_shared<CustomCtrl>("Hi");
app().registerController(ctrlPtr);
//Install custom filter
auto filterPtr = std::make_shared<CustomHeaderFilter>("custom_header", "yes");
app().registerFilter(filterPtr);
app().run();
}

View File

@ -532,7 +532,32 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro, bool isHttps =
exit(1);
}
});
/// Test controllers created and initialized by users
req = HttpRequest::newHttpFormPostRequest();
req->setMethod(drogon::Get);
req->setPath("/customctrl/antao");
req->addHeader("custom_header", "yes");
client->sendRequest(req, [=](ReqResult result, const HttpResponsePtr &resp) {
if (result == ReqResult::Ok)
{
auto ret = resp->getBody();
if (ret == "<P>Hi, antao</P>")
{
outputGood(req, isHttps);
}
else
{
LOG_DEBUG << resp->getBody();
LOG_ERROR << "Error!";
exit(1);
}
}
else
{
LOG_ERROR << "Error!";
exit(1);
}
});
/// Test form post
req = HttpRequest::newHttpFormPostRequest();
req->setPath("/api/v1/apitest/form");

View File

@ -14,14 +14,15 @@
#pragma once
#include <drogon/utils/ClassTraits.h>
#include <stdio.h>
#include <unordered_map>
#include <memory>
#include <functional>
#include <thread>
#include <mutex>
#include <vector>
#include <cxxabi.h>
namespace drogon
{
@ -34,7 +35,22 @@ class DrClassMap
static void registerClass(const std::string &className, const DrAllocFunc &func);
static DrObjectBase *newObject(const std::string &className);
static const std::shared_ptr<DrObjectBase> &getSingleInstance(const std::string &className);
template <typename T>
static std::shared_ptr<T> getSingleInstance()
{
static_assert(internal::IsSubClass<T, DrObjectBase>::value, "T must be a sub-class of DrObjectBase");
return std::dynamic_pointer_cast<T>(getSingleInstance(T::classTypeName()));
}
static void setSingleInstance(const std::shared_ptr<DrObjectBase> &ins);
static std::vector<std::string> getAllClassName();
static const std::string demangle(const char *mangled_name)
{
std::size_t len = 0;
int status = 0;
std::unique_ptr<char, decltype(&std::free)> ptr(
__cxxabiv1::__cxa_demangle(mangled_name, nullptr, &len, &status), &std::free);
return ptr.get();
}
protected:
static std::unordered_map<std::string, DrAllocFunc> &getMap();

View File

@ -16,8 +16,9 @@
#include <drogon/DrClassMap.h>
#include <cxxabi.h>
#include <string>
#include <type_traits>
namespace drogon
{
class DrObjectBase
@ -32,14 +33,6 @@ class DrObjectBase
{
return (className() == class_name);
}
static const std::string demangle(const char *mangled_name)
{
std::size_t len = 0;
int status = 0;
std::unique_ptr<char, decltype(&std::free)> ptr(
__cxxabiv1::__cxa_demangle(mangled_name, nullptr, &len, &status), &std::free);
return ptr.get();
}
virtual ~DrObjectBase() {}
};
@ -74,15 +67,24 @@ class DrObject : public virtual DrObjectBase
{
public:
DrAllocator()
{
registerClass<T>();
}
const std::string &className() const
{
static std::string className = DrClassMap::demangle(typeid(T).name());
return className;
}
template <typename D>
typename std::enable_if<std::is_default_constructible<D>::value, void>::type registerClass()
{
DrClassMap::registerClass(className(), []() -> DrObjectBase * {
return new T;
});
}
const std::string &className() const
template <typename D>
typename std::enable_if<!std::is_default_constructible<D>::value, void>::type registerClass()
{
static std::string className = demangle(typeid(T).name());
return className;
}
};

View File

@ -21,6 +21,7 @@
namespace drogon
{
typedef HttpViewData DrTemplateData;
/// The templating engine class
@ -35,7 +36,7 @@ class DrTemplateBase : public virtual DrObjectBase
/**
* The @param templateName represents the name of the template file.
* A template file is a description file with a special format. Its extension is
* usually .csp and can be omitted. The user should preprocess the template file
* usually .csp. The user should preprocess the template file
* with the drogon_ctl tool to create c++ source files.
*/
static std::shared_ptr<DrTemplateBase> newTemplate(std::string templateName);
@ -49,4 +50,5 @@ class DrTemplateBase : public virtual DrObjectBase
virtual ~DrTemplateBase(){};
DrTemplateBase(){};
};
} // namespace drogon

View File

@ -19,6 +19,7 @@
#include <drogon/orm/DbClient.h>
#endif
#include <drogon/utils/Utilities.h>
#include <drogon/utils/ClassTraits.h>
#include <drogon/HttpBinder.h>
#include <trantor/utils/NonCopyable.h>
#include <drogon/DrObject.h>
@ -58,6 +59,10 @@ inline std::string getGitCommit()
return VERSION_MD5;
}
class HttpControllerBase;
class HttpSimpleControllerBase;
class WebSocketControllerBase;
class HttpAppFramework : public trantor::NonCopyable
{
public:
@ -115,7 +120,7 @@ class HttpAppFramework : public trantor::NonCopyable
{
static_assert(IsPlugin<T>::value, "The Template parameter must be a subclass of PluginBase");
assert(isRunning());
return dynamic_cast<T *>(getPlugin(T::className()));
return dynamic_cast<T *>(getPlugin(T::classTypeName()));
}
///Get the plugin object registered in the framework
@ -216,6 +221,54 @@ class HttpAppFramework : public trantor::NonCopyable
const std::vector<std::string> &filters =
std::vector<std::string>()) = 0;
/// Register controller objects created and initialized by the user
/**
* Drogon can only automatically create controllers using the default constructor.
* Sometimes users want to be able to create controllers using constructors with
* parameters. Controllers created by user in this way should be registered to the framework
* via this method.
* The macro or configuration file is still valid for the path routing configuration
* of the controller created by users.
*
* NOTE:
* The declaration of the controller class must be as follows:
* class ApiTest : public drogon::HttpController<ApiTest, false>
* {
* public:
* ApiTest(const std::string &str);
* ...
* };
* The second template parameter must be explicitly set to false to disable automatic creation.
* And then user can create and register it somewhere as follows:
* auto ctrlPtr=std::make_shared<ApiTest>("hello world");
* drogon::app().registerController(ctrlPtr);
* This method should be called before calling the app().run() method.
*/
template <typename T>
void registerController(const std::shared_ptr<T> &ctrlPtr)
{
static_assert(internal::IsSubClass<T, HttpControllerBase>::value ||
internal::IsSubClass<T, HttpSimpleControllerBase>::value ||
internal::IsSubClass<T, WebSocketControllerBase>::value,
"Error! Only controller objects can be registered here");
static_assert(!T::isAutoCreation, "Controllers created and initialized automatically by drogon cannot be registered here");
DrClassMap::setSingleInstance(ctrlPtr);
T::initPathRouting();
}
/// Register filter objects created and initialized by the user
/**
* This method is similar to the above method.
*/
template <typename T>
void registerFilter(const std::shared_ptr<T> &filterPtr)
{
static_assert(internal::IsSubClass<T, HttpFilterBase>::value,
"Error! Only fitler objects can be registered here");
static_assert(!T::isAutoCreation, "Filters created and initialized automatically by drogon cannot be registered here");
DrClassMap::setSingleInstance(filterPtr);
}
///Get the custom configuration defined by users in the configuration file.
virtual const Json::Value &getCustomConfig() const = 0;

View File

@ -17,6 +17,7 @@
#include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h>
#include <drogon/utils/FunctionTraits.h>
#include <drogon/DrClassMap.h>
#include <drogon/DrObject.h>
#include <list>
#include <string>
@ -109,15 +110,13 @@ class HttpBinder : public HttpBinderBase
FUNCTION _func;
typedef FunctionTraits<FUNCTION> traits;
template <
std::size_t Index>
template <std::size_t Index>
using nth_argument_type = typename traits::template argument<Index>;
static const size_t argument_count = traits::arity;
template <
typename... Values,
std::size_t Boundary = argument_count>
template <typename... Values,
std::size_t Boundary = argument_count>
typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run(
std::list<std::string> &pathParameter,
const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> callback,
@ -152,14 +151,25 @@ class HttpBinder : public HttpBinderBase
callFunction(req, callback, std::move(values)...);
}
template <typename... Values,
bool isClassFunction = traits::isClassFunction>
typename std::enable_if<isClassFunction, void>::type callFunction(
bool isClassFunction = traits::isClassFunction,
bool isDrObjectClass = traits::isDrObjectClass>
typename std::enable_if<isClassFunction && !isDrObjectClass, void>::type callFunction(
const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> callback,
Values &&... values)
{
auto &obj = getControllerObj<typename traits::class_type>();
static auto &obj = getControllerObj<typename traits::class_type>();
(obj.*_func)(req, callback, std::move(values)...);
};
template <typename... Values,
bool isClassFunction = traits::isClassFunction,
bool isDrObjectClass = traits::isDrObjectClass>
typename std::enable_if<isClassFunction && isDrObjectClass, void>::type callFunction(
const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> callback,
Values &&... values)
{
static auto objPtr = DrClassMap::getSingleInstance<typename traits::class_type>();
(*objPtr.*_func)(req, callback, std::move(values)...);
};
template <typename... Values,
bool isClassFunction = traits::isClassFunction>
typename std::enable_if<!isClassFunction, void>::type callFunction(

View File

@ -23,8 +23,8 @@
/// For more details on the class, see the wiki site (the 'HttpController' section)
#define METHOD_LIST_BEGIN \
static void initMethods() \
#define METHOD_LIST_BEGIN \
static void initPathRouting() \
{
#define METHOD_ADD(method, pattern, filters...) \
@ -34,15 +34,21 @@
#define METHOD_LIST_END \
return; \
} \
\
protected:
}
namespace drogon
{
template <typename T>
class HttpController : public DrObject<T>
class HttpControllerBase
{
};
template <typename T, bool AutoCreation = true>
class HttpController : public DrObject<T>, public HttpControllerBase
{
public:
static const bool isAutoCreation = AutoCreation;
protected:
template <typename FUNCTION>
static void registerMethod(FUNCTION &&function,
@ -60,12 +66,12 @@ class HttpController : public DrObject<T>
}
if (pattern.empty() || pattern[0] == '/')
app().registerHandler(path + pattern,
std::forward<FUNCTION>(function),
filtersAndMethods);
std::forward<FUNCTION>(function),
filtersAndMethods);
else
app().registerHandler(path + "/" + pattern,
std::forward<FUNCTION>(function),
filtersAndMethods);
std::forward<FUNCTION>(function),
filtersAndMethods);
}
private:
@ -74,7 +80,8 @@ class HttpController : public DrObject<T>
public:
methodRegister()
{
T::initMethods();
if (AutoCreation)
T::initPathRouting();
}
};
//use static value to register controller method in framework before main();
@ -84,6 +91,6 @@ class HttpController : public DrObject<T>
return &_register;
}
};
template <typename T>
typename HttpController<T>::methodRegister HttpController<T>::_register;
template <typename T, bool AutoCreation>
typename HttpController<T, AutoCreation>::methodRegister HttpController<T, AutoCreation>::_register;
} // namespace drogon

View File

@ -33,10 +33,11 @@ class HttpFilterBase : public virtual DrObjectBase
const FilterChainCallback &fccb) = 0;
virtual ~HttpFilterBase() {}
};
template <typename T>
template <typename T, bool AutoCreation = true>
class HttpFilter : public DrObject<T>, public HttpFilterBase
{
public:
static const bool isAutoCreation = AutoCreation;
virtual ~HttpFilter() {}
};
} // namespace drogon

View File

@ -20,8 +20,8 @@
#include <string>
#include <vector>
#include <iostream>
#define PATH_LIST_BEGIN \
static void ___paths___() \
#define PATH_LIST_BEGIN \
static void initPathRouting() \
{
#define PATH_ADD(path, filters...) __registerSelf(path, {filters});
@ -30,7 +30,7 @@
}
namespace drogon
{
class HttpSimpleControllerBase : public virtual DrObjectBase
{
public:
@ -38,10 +38,11 @@ class HttpSimpleControllerBase : public virtual DrObjectBase
virtual ~HttpSimpleControllerBase() {}
};
template <typename T>
template <typename T, bool AutoCreation = true>
class HttpSimpleController : public DrObject<T>, public HttpSimpleControllerBase
{
public:
static const bool isAutoCreation = AutoCreation;
virtual ~HttpSimpleController() {}
protected:
@ -58,16 +59,11 @@ class HttpSimpleController : public DrObject<T>, public HttpSimpleControllerBase
public:
pathRegister()
{
T::___paths___();
if (AutoCreation)
{
T::initPathRouting();
}
}
// protected:
// void _register(const std::string &className, const std::vector<std::string> &paths)
// {
// for (auto const &reqPath : paths)
// {
// std::cout << "register controller class " << className << " on path " << reqPath << std::endl;
// }
// }
};
friend pathRegister;
static pathRegister _register;
@ -76,7 +72,7 @@ class HttpSimpleController : public DrObject<T>, public HttpSimpleControllerBase
return &_register;
}
};
template <typename T>
typename HttpSimpleController<T>::pathRegister HttpSimpleController<T>::_register;
template <typename T, bool AutoCreation>
typename HttpSimpleController<T, AutoCreation>::pathRegister HttpSimpleController<T, AutoCreation>::_register;
} // namespace drogon

View File

@ -23,9 +23,9 @@
#include <vector>
#include <iostream>
#include <memory>
#define WS_PATH_LIST_BEGIN \
static std::vector<std::pair<std::string, std::vector<std::string>>> paths() \
{ \
#define WS_PATH_LIST_BEGIN \
static std::vector<std::pair<std::string, std::vector<std::string>>> __paths() \
{ \
std::vector<std::pair<std::string, std::vector<std::string>>> vet;
#define WS_PATH_ADD(path, filters...) \
@ -34,8 +34,10 @@
#define WS_PATH_LIST_END \
return vet; \
}
namespace drogon
{
class WebSocketControllerBase : public virtual DrObjectBase
{
public:
@ -51,11 +53,24 @@ class WebSocketControllerBase : public virtual DrObjectBase
};
typedef std::shared_ptr<WebSocketControllerBase> WebSocketControllerBasePtr;
template <typename T>
template <typename T, bool AutoCreation = true>
class WebSocketController : public DrObject<T>, public WebSocketControllerBase
{
public:
static const bool isAutoCreation = AutoCreation;
virtual ~WebSocketController() {}
static void initPathRouting()
{
auto vPaths = T::__paths();
for (auto const &path : vPaths)
{
LOG_TRACE << "register websocket controller (" << WebSocketController<T>::classTypeName() << ") on path:" << path.first;
HttpAppFramework::instance().registerWebSocketController(path.first,
WebSocketController<T>::classTypeName(),
path.second);
}
}
protected:
WebSocketController() {}
@ -66,25 +81,11 @@ class WebSocketController : public DrObject<T>, public WebSocketControllerBase
public:
pathRegister()
{
auto vPaths = T::paths();
for (auto const &path : vPaths)
if (AutoCreation)
{
LOG_TRACE << "register websocket controller (" << WebSocketController<T>::classTypeName() << ") on path:" << path.first;
HttpAppFramework::instance().registerWebSocketController(path.first,
WebSocketController<T>::classTypeName(),
path.second);
T::initPathRouting();
}
}
// protected:
// void _register(const std::string &className, const std::vector<std::string> &paths)
// {
// for (auto const &reqPath : paths)
// {
// std::cout << "register controller class " << className << " on path " << reqPath << std::endl;
// }
// }
};
friend pathRegister;
static pathRegister _register;
@ -93,7 +94,7 @@ class WebSocketController : public DrObject<T>, public WebSocketControllerBase
return &_register;
}
};
template <typename T>
typename WebSocketController<T>::pathRegister WebSocketController<T>::_register;
template <typename T, bool AutoCreation>
typename WebSocketController<T, AutoCreation>::pathRegister WebSocketController<T, AutoCreation>::_register;
} // namespace drogon

View File

@ -0,0 +1,34 @@
/**
*
* ClassTraits.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include <type_traits>
namespace drogon
{
namespace internal
{
/// This template is used to check whether S is a subclass of B.
template <typename S, typename B>
struct IsSubClass
{
typedef typename std::remove_cv<typename std::remove_reference<S>::type>::type SubType;
typedef typename std::remove_cv<typename std::remove_reference<B>::type>::type BaseType;
static char test(void *) { return 0; }
static int test(BaseType *) { return 0; }
static const bool value = (sizeof(test((SubType *)nullptr)) == sizeof(int));
};
} // namespace internal
} // namespace drogon

View File

@ -14,6 +14,8 @@
#pragma once
#include <drogon/DrObject.h>
#include <drogon/utils/ClassTraits.h>
#include <tuple>
#include <type_traits>
#include <memory>
@ -38,6 +40,7 @@ struct FunctionTraits : public FunctionTraits<
decltype(&std::remove_reference<Function>::type::operator())>
{
static const bool isClassFunction = false;
static const bool isDrObjectClass = false;
typedef void class_type;
static const std::string name()
{
@ -46,14 +49,13 @@ struct FunctionTraits : public FunctionTraits<
};
//class instance method of const object
template <
typename ClassType,
typename ReturnType,
typename... Arguments>
struct FunctionTraits<
ReturnType (ClassType::*)(Arguments...) const> : FunctionTraits<ReturnType (*)(Arguments...)>
template <typename ClassType,
typename ReturnType,
typename... Arguments>
struct FunctionTraits<ReturnType (ClassType::*)(Arguments...) const> : FunctionTraits<ReturnType (*)(Arguments...)>
{
static const bool isClassFunction = true;
static const bool isDrObjectClass = IsSubClass<ClassType, DrObject<ClassType>>::value;
typedef ClassType class_type;
static const std::string name() { return std::string("Class Function"); }
};
@ -63,10 +65,10 @@ template <
typename ClassType,
typename ReturnType,
typename... Arguments>
struct FunctionTraits<
ReturnType (ClassType::*)(Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>
struct FunctionTraits<ReturnType (ClassType::*)(Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>
{
static const bool isClassFunction = true;
static const bool isDrObjectClass = IsSubClass<ClassType, DrObject<ClassType>>::value;
typedef ClassType class_type;
static const std::string name() { return std::string("Class Function"); }
};
@ -100,6 +102,7 @@ struct FunctionTraits<
typedef void class_type;
static const bool isHTTPFunction = false;
static const bool isClassFunction = false;
static const bool isDrObjectClass = false;
static const std::string name() { return std::string("Normal or Static Function"); }
};

View File

@ -18,12 +18,31 @@
using namespace drogon;
//std::map <std::string,DrAllocFunc> * DrClassMap::classMap=nullptr;
//std::once_flag DrClassMap::flag;
namespace drogon
{
namespace internal
{
static std::unordered_map<std::string, std::shared_ptr<DrObjectBase>> &getObjsMap()
{
static std::unordered_map<std::string, std::shared_ptr<DrObjectBase>> singleInstanceMap;
return singleInstanceMap;
}
static std::mutex &getMapMutex()
{
static std::mutex mtx;
return mtx;
}
} // namespace internal
} // namespace drogon
void DrClassMap::registerClass(const std::string &className, const DrAllocFunc &func)
{
//std::cout<<"register class:"<<className<<std::endl;
getMap().insert(std::make_pair(className, func));
}
DrObjectBase *DrClassMap::newObject(const std::string &className)
{
auto iter = getMap().find(className);
@ -34,10 +53,11 @@ DrObjectBase *DrClassMap::newObject(const std::string &className)
else
return nullptr;
}
const std::shared_ptr<DrObjectBase> &DrClassMap::getSingleInstance(const std::string &className)
{
static std::unordered_map<std::string, std::shared_ptr<DrObjectBase>> singleInstanceMap;
static std::mutex mtx;
auto &mtx = internal::getMapMutex();
auto &singleInstanceMap = internal::getObjsMap();
std::lock_guard<std::mutex> lock(mtx);
auto iter = singleInstanceMap.find(className);
if (iter != singleInstanceMap.end())
@ -45,6 +65,15 @@ const std::shared_ptr<DrObjectBase> &DrClassMap::getSingleInstance(const std::st
singleInstanceMap[className] = std::shared_ptr<DrObjectBase>(newObject(className));
return singleInstanceMap[className];
}
void DrClassMap::setSingleInstance(const std::shared_ptr<DrObjectBase> &ins)
{
auto &mtx = internal::getMapMutex();
auto &singleInstanceMap = internal::getObjsMap();
std::lock_guard<std::mutex> lock(mtx);
singleInstanceMap[ins->className()] = ins;
}
std::vector<std::string> DrClassMap::getAllClassName()
{
std::vector<std::string> ret;
@ -54,6 +83,7 @@ std::vector<std::string> DrClassMap::getAllClassName()
}
return ret;
}
std::unordered_map<std::string, DrAllocFunc> &DrClassMap::getMap()
{
static std::unordered_map<std::string, DrAllocFunc> map;

View File

@ -30,7 +30,7 @@ void WebsocketControllersRouter::registerWebSocketController(const std::string &
assert(!ctrlName.empty());
std::string path(pathName);
std::transform(pathName.begin(), pathName.end(), path.begin(), tolower);
auto objPtr = std::shared_ptr<DrObjectBase>(DrClassMap::newObject(ctrlName));
auto objPtr = DrClassMap::getSingleInstance(ctrlName);
auto ctrlPtr = std::dynamic_pointer_cast<WebSocketControllerBase>(objPtr);
assert(ctrlPtr);
std::lock_guard<std::mutex> guard(_websockCtrlMutex);