Compare commits

...

7 Commits

Author SHA1 Message Date
Martin Chang
e3bc481a5e Merge branch 'h2-client' of github.com:drogonframework/drogon into h2-client 2023-11-21 10:58:32 +08:00
Martin Chang
1f78acd89e Merge remote-tracking branch 'origin/master' into h2-client 2023-11-21 10:58:12 +08:00
Vinicius
26840aa056
Fix: uuid formatting (#1854)
Co-authored-by: root <vinicts@protonmail.com>
Co-authored-by: an-tao <antao2002@gmail.com>
2023-11-18 16:23:13 +08:00
George Constantinides
01385f4f33
Update test_cmake.csp (#1856)
_test not needed for submodule target_link_libraries since it was added in project
2023-11-18 16:13:30 +08:00
Muhammad
2000a4279e
Make id generator consistent (#1851) 2023-11-18 15:46:59 +08:00
An Tao
1ec3c96cbb
Use the constexpr if instead of std::enable_if (#1843) 2023-11-15 11:22:14 +08:00
Martin Chang
0ec5b00f86 remove bad warning 2023-11-12 10:43:56 +08:00
24 changed files with 750 additions and 729 deletions

View File

@ -6,9 +6,9 @@ add_executable(${PROJECT_NAME} test_main.cc)
# ############################################################################## # ##############################################################################
# If you include the drogon source code locally in your project, use this method # If you include the drogon source code locally in your project, use this method
# to add drogon # to add drogon
# target_link_libraries(${PROJECT_NAME}_test PRIVATE drogon) # target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
# #
# and comment out the following lines # and comment out the following lines
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon) target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
ParseAndAddDrogonTests(${PROJECT_NAME}) ParseAndAddDrogonTests(${PROJECT_NAME})

View File

@ -57,6 +57,26 @@ class DROGON_EXPORT DrObjectBase
} }
}; };
template <typename T>
struct isAutoCreationClass
{
template <class C>
static constexpr auto check(C *)
-> std::enable_if_t<std::is_same_v<decltype(C::isAutoCreation), bool>,
bool>
{
return C::isAutoCreation;
}
template <typename>
static constexpr bool check(...)
{
return false;
}
static constexpr bool value = check<T>(nullptr);
};
/** /**
* a class template to * a class template to
* implement the reflection function of creating the class object by class name * implement the reflection function of creating the class object by class name
@ -102,23 +122,22 @@ class DrObject : public virtual DrObjectBase
} }
template <typename D> template <typename D>
typename std::enable_if<std::is_default_constructible<D>::value, void registerClass()
void>::type
registerClass()
{
DrClassMap::registerClass(
className(),
[]() -> DrObjectBase * { return new T; },
[]() -> std::shared_ptr<DrObjectBase> {
return std::make_shared<T>();
});
}
template <typename D>
typename std::enable_if<!std::is_default_constructible<D>::value,
void>::type
registerClass()
{ {
if constexpr (std::is_default_constructible<D>::value)
{
DrClassMap::registerClass(
className(),
[]() -> DrObjectBase * { return new T; },
[]() -> std::shared_ptr<DrObjectBase> {
return std::make_shared<T>();
});
}
else if constexpr (isAutoCreationClass<D>::value)
{
static_assert(std::is_default_constructible<D>::value,
"Class is not default constructable!");
}
} }
}; };

View File

@ -70,6 +70,91 @@ struct BinderArgTypeTraits<const T &>
static const bool isValid = true; static const bool isValid = true;
}; };
template <typename T>
T getHandlerArgumentValue(std::string &&p)
{
if constexpr (internal::CanConstructFromString<T>::value)
{
return T(std::move(p));
}
else if constexpr (internal::CanConvertFromStringStream<T>::value)
{
T value{T()};
if (!p.empty())
{
std::stringstream ss(std::move(p));
ss >> value;
}
return value;
}
else if constexpr (internal::CanConvertFromString<T>::value)
{
T value;
value = p;
return value;
}
else
{
LOG_ERROR << "Can't convert string to type " << typeid(T).name();
return T();
}
}
template <>
inline std::string getHandlerArgumentValue<std::string>(std::string &&p)
{
return std::move(p);
}
template <>
inline int getHandlerArgumentValue<int>(std::string &&p)
{
return std::stoi(p);
}
template <>
inline long getHandlerArgumentValue<long>(std::string &&p)
{
return std::stol(p);
}
template <>
inline long long getHandlerArgumentValue<long long>(std::string &&p)
{
return std::stoll(p);
}
template <>
inline unsigned long getHandlerArgumentValue<unsigned long>(std::string &&p)
{
return std::stoul(p);
}
template <>
inline unsigned long long getHandlerArgumentValue<unsigned long long>(
std::string &&p)
{
return std::stoull(p);
}
template <>
inline float getHandlerArgumentValue<float>(std::string &&p)
{
return std::stof(p);
}
template <>
inline double getHandlerArgumentValue<double>(std::string &&p)
{
return std::stod(p);
}
template <>
inline long double getHandlerArgumentValue<long double>(std::string &&p)
{
return std::stold(p);
}
class HttpBinderBase class HttpBinderBase
{ {
public: public:
@ -153,27 +238,22 @@ class HttpBinder : public HttpBinderBase
template <bool isClassFunction = traits::isClassFunction, template <bool isClassFunction = traits::isClassFunction,
bool isDrObjectClass = traits::isDrObjectClass> bool isDrObjectClass = traits::isDrObjectClass>
typename std::enable_if<isDrObjectClass && isClassFunction, void>::type void createHandlerInstance()
createHandlerInstance()
{
auto objPtr =
DrClassMap::getSingleInstance<typename traits::class_type>();
LOG_TRACE << "create handler class object: " << objPtr.get();
}
template <bool isClassFunction = traits::isClassFunction,
bool isDrObjectClass = traits::isDrObjectClass>
typename std::enable_if<!isDrObjectClass && isClassFunction, void>::type
createHandlerInstance()
{
auto &obj = getControllerObj<typename traits::class_type>();
LOG_TRACE << "create handler class object: " << &obj;
}
template <bool isClassFunction = traits::isClassFunction>
typename std::enable_if<!isClassFunction, void>::type
createHandlerInstance()
{ {
if constexpr (isClassFunction)
{
if constexpr (isDrObjectClass)
{
auto objPtr = DrClassMap::getSingleInstance<
typename traits::class_type>();
LOG_TRACE << "create handler class object: " << objPtr.get();
}
else
{
auto &obj = getControllerObj<typename traits::class_type>();
LOG_TRACE << "create handler class object: " << &obj;
}
}
} }
private: private:
@ -184,276 +264,201 @@ class HttpBinder : public HttpBinderBase
static const size_t argument_count = traits::arity; static const size_t argument_count = traits::arity;
std::string handlerName_; std::string handlerName_;
template <typename T>
typename std::enable_if<internal::CanConvertFromStringStream<T>::value,
void>::type
getHandlerArgumentValue(T &value, std::string &&p)
{
if (!p.empty())
{
std::stringstream ss(std::move(p));
ss >> value;
}
}
template <typename T>
typename std::enable_if<!(internal::CanConvertFromStringStream<T>::value),
void>::type
getHandlerArgumentValue(T &value, std::string &&p)
{
}
void getHandlerArgumentValue(std::string &value, std::string &&p)
{
value = std::move(p);
}
void getHandlerArgumentValue(int &value, std::string &&p)
{
value = std::stoi(p);
}
void getHandlerArgumentValue(long &value, std::string &&p)
{
value = std::stol(p);
}
void getHandlerArgumentValue(long long &value, std::string &&p)
{
value = std::stoll(p);
}
void getHandlerArgumentValue(unsigned long &value, std::string &&p)
{
value = std::stoul(p);
}
void getHandlerArgumentValue(unsigned long long &value, std::string &&p)
{
value = std::stoull(p);
}
void getHandlerArgumentValue(float &value, std::string &&p)
{
value = std::stof(p);
}
void getHandlerArgumentValue(double &value, std::string &&p)
{
value = std::stod(p);
}
void getHandlerArgumentValue(long double &value, std::string &&p)
{
value = std::stold(p);
}
template <typename... Values, std::size_t Boundary = argument_count>
typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run(
std::deque<std::string> &pathArguments,
const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback,
Values &&...values)
{
// Call this function recursively until parameter's count equals to the
// count of target function parameters
static_assert(
BinderArgTypeTraits<nth_argument_type<sizeof...(Values)>>::isValid,
"your handler argument type must be value type or const left "
"reference type or right reference type");
using ValueType =
typename std::remove_cv<typename std::remove_reference<
nth_argument_type<sizeof...(Values)>>::type>::type;
ValueType value = ValueType();
if (!pathArguments.empty())
{
std::string v = std::move(pathArguments.front());
pathArguments.pop_front();
try
{
if (v.empty() == false)
getHandlerArgumentValue(value, std::move(v));
}
catch (const std::exception &e)
{
handleException(e, req, std::move(callback));
return;
}
}
else
{
try
{
value = req->as<ValueType>();
}
catch (const std::exception &e)
{
handleException(e, req, std::move(callback));
return;
}
catch (...)
{
LOG_ERROR << "Exception not derived from std::exception";
return;
}
}
run(pathArguments,
req,
std::move(callback),
std::forward<Values>(values)...,
std::move(value));
}
template <typename... Values, template <typename... Values,
std::size_t Boundary = argument_count, std::size_t Boundary = argument_count,
bool isCoroutine = traits::isCoroutine> bool isCoroutine = traits::isCoroutine>
typename std::enable_if<(sizeof...(Values) == Boundary) && !isCoroutine, void run(std::deque<std::string> &pathArguments,
void>::type const HttpRequestPtr &req,
run(std::deque<std::string> &, std::function<void(const HttpResponsePtr &)> &&callback,
const HttpRequestPtr &req, Values &&...values)
std::function<void(const HttpResponsePtr &)> &&callback,
Values &&...values)
{ {
try if constexpr (sizeof...(Values) < Boundary)
{ { // Call this function recursively until parameter's count equals to
// Explcit copy because `callFunction` moves it // the count of target function parameters
auto cb = callback; static_assert(
callFunction(req, cb, std::move(values)...); BinderArgTypeTraits<
} nth_argument_type<sizeof...(Values)>>::isValid,
catch (const std::exception &except) "your handler argument type must be value type or const left "
{ "reference type or right reference type");
handleException(except, req, std::move(callback)); using ValueType = std::remove_cv_t<
} std::remove_reference_t<nth_argument_type<sizeof...(Values)>>>;
catch (...) if (!pathArguments.empty())
{
LOG_ERROR << "Exception not derived from std::exception";
return;
}
}
#ifdef __cpp_impl_coroutine
template <typename... Values,
std::size_t Boundary = argument_count,
bool isCoroutine = traits::isCoroutine>
typename std::enable_if<(sizeof...(Values) == Boundary) && isCoroutine,
void>::type
run(std::deque<std::string> &,
const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback,
Values &&...values)
{
[this](HttpRequestPtr req,
std::function<void(const HttpResponsePtr &)> callback,
Values &&...values) -> AsyncTask {
try
{ {
if constexpr (std::is_same_v<AsyncTask, std::string v{std::move(pathArguments.front())};
typename traits::return_type>) pathArguments.pop_front();
try
{
if (!v.empty())
{
auto value =
getHandlerArgumentValue<ValueType>(std::move(v));
run(pathArguments,
req,
std::move(callback),
std::forward<Values>(values)...,
std::move(value));
return;
}
}
catch (const std::exception &e)
{
handleException(e, req, std::move(callback));
return;
}
}
else
{
try
{
auto value = req->as<ValueType>();
run(pathArguments,
req,
std::move(callback),
std::forward<Values>(values)...,
std::move(value));
return;
}
catch (const std::exception &e)
{
handleException(e, req, std::move(callback));
return;
}
catch (...)
{
LOG_ERROR << "Exception not derived from std::exception";
return;
}
}
run(pathArguments,
req,
std::move(callback),
std::forward<Values>(values)...,
ValueType());
}
else if constexpr (sizeof...(Values) == Boundary)
{
if constexpr (!isCoroutine)
{
try
{ {
// Explcit copy because `callFunction` moves it // Explcit copy because `callFunction` moves it
auto cb = callback; auto cb = callback;
callFunction(req, cb, std::move(values)...); callFunction(req, cb, std::move(values)...);
} }
else if constexpr (std::is_same_v<Task<>, catch (const std::exception &except)
typename traits::return_type>)
{ {
// Explcit copy because `callFunction` moves it handleException(except, req, std::move(callback));
auto cb = callback;
co_await callFunction(req, cb, std::move(values)...);
} }
else if constexpr (std::is_same_v<Task<HttpResponsePtr>, catch (...)
typename traits::return_type>)
{ {
auto resp = LOG_ERROR << "Exception not derived from std::exception";
co_await callFunction(req, std::move(values)...); return;
callback(std::move(resp));
} }
} }
catch (const std::exception &except) #ifdef __cpp_impl_coroutine
else
{ {
handleException(except, req, std::move(callback)); [this](HttpRequestPtr req,
std::function<void(const HttpResponsePtr &)> callback,
Values &&...values) -> AsyncTask {
try
{
if constexpr (std::is_same_v<
AsyncTask,
typename traits::return_type>)
{
// Explcit copy because `callFunction` moves it
auto cb = callback;
callFunction(req, cb, std::move(values)...);
}
else if constexpr (std::is_same_v<
Task<>,
typename traits::return_type>)
{
// Explcit copy because `callFunction` moves it
auto cb = callback;
co_await callFunction(req,
cb,
std::move(values)...);
}
else if constexpr (std::is_same_v<
Task<HttpResponsePtr>,
typename traits::return_type>)
{
auto resp =
co_await callFunction(req,
std::move(values)...);
callback(std::move(resp));
}
}
catch (const std::exception &except)
{
handleException(except, req, std::move(callback));
}
catch (...)
{
LOG_ERROR
<< "Exception not derived from std::exception";
}
}(req, std::move(callback), std::move(values)...);
} }
catch (...)
{
LOG_ERROR << "Exception not derived from std::exception";
}
}(req, std::move(callback), std::move(values)...);
}
#endif #endif
template <typename... Values, }
bool isClassFunction = traits::isClassFunction,
bool isDrObjectClass = traits::isDrObjectClass,
bool isNormal = std::is_same<typename traits::first_param_type,
HttpRequestPtr>::value>
typename std::enable_if<isClassFunction && !isDrObjectClass && isNormal,
typename traits::return_type>::type
callFunction(const HttpRequestPtr &req, Values &&...values)
{
static auto &obj = getControllerObj<typename traits::class_type>();
return (obj.*func_)(req, std::move(values)...);
} }
template <typename... Values, template <typename... Values,
bool isClassFunction = traits::isClassFunction, bool isClassFunction = traits::isClassFunction,
bool isDrObjectClass = traits::isDrObjectClass, bool isDrObjectClass = traits::isDrObjectClass,
bool isNormal = std::is_same<typename traits::first_param_type, bool isNormal = std::is_same_v<typename traits::first_param_type,
HttpRequestPtr>::value> HttpRequestPtr>>
typename std::enable_if<isClassFunction && isDrObjectClass && isNormal, typename traits::return_type callFunction(const HttpRequestPtr &req,
typename traits::return_type>::type Values &&...values)
callFunction(const HttpRequestPtr &req, Values &&...values)
{ {
static auto objPtr = if constexpr (isNormal)
DrClassMap::getSingleInstance<typename traits::class_type>(); {
return (*objPtr.*func_)(req, std::move(values)...); if constexpr (isClassFunction)
} {
if constexpr (!isDrObjectClass)
template <typename... Values, {
bool isClassFunction = traits::isClassFunction, static auto &obj =
bool isNormal = std::is_same<typename traits::first_param_type, getControllerObj<typename traits::class_type>();
HttpRequestPtr>::value> return (obj.*func_)(req, std::move(values)...);
typename std::enable_if<!isClassFunction && isNormal, }
typename traits::return_type>::type else
callFunction(const HttpRequestPtr &req, Values &&...values) {
{ static auto objPtr = DrClassMap::getSingleInstance<
return func_(req, std::move(values)...); typename traits::class_type>();
} return (*objPtr.*func_)(req, std::move(values)...);
}
template <typename... Values, }
bool isClassFunction = traits::isClassFunction, else
bool isDrObjectClass = traits::isDrObjectClass, {
bool isNormal = std::is_same<typename traits::first_param_type, return func_(req, std::move(values)...);
HttpRequestPtr>::value> }
typename std::enable_if<isClassFunction && !isDrObjectClass && !isNormal, }
typename traits::return_type>::type else
callFunction(const HttpRequestPtr &req, Values &&...values) {
{ if constexpr (isClassFunction)
static auto &obj = getControllerObj<typename traits::class_type>(); {
return (obj.*func_)((*req), std::move(values)...); if constexpr (!isDrObjectClass)
} {
static auto &obj =
template <typename... Values, getControllerObj<typename traits::class_type>();
bool isClassFunction = traits::isClassFunction, return (obj.*func_)((*req), std::move(values)...);
bool isDrObjectClass = traits::isDrObjectClass, }
bool isNormal = std::is_same<typename traits::first_param_type, else
HttpRequestPtr>::value> {
typename std::enable_if<isClassFunction && isDrObjectClass && !isNormal, static auto objPtr = DrClassMap::getSingleInstance<
typename traits::return_type>::type typename traits::class_type>();
callFunction(const HttpRequestPtr &req, Values &&...values) return (*objPtr.*func_)((*req), std::move(values)...);
{ }
static auto objPtr = }
DrClassMap::getSingleInstance<typename traits::class_type>(); else
return (*objPtr.*func_)((*req), std::move(values)...); {
} return func_((*req), std::move(values)...);
}
template <typename... Values, }
bool isClassFunction = traits::isClassFunction,
bool isNormal = std::is_same<typename traits::first_param_type,
HttpRequestPtr>::value>
typename std::enable_if<!isClassFunction && !isNormal,
typename traits::return_type>::type
callFunction(const HttpRequestPtr &req, Values &&...values)
{
return func_((*req), std::move(values)...);
} }
}; };

View File

@ -59,7 +59,7 @@ template <typename T, bool AutoCreation = true>
class HttpController : public DrObject<T>, public HttpControllerBase class HttpController : public DrObject<T>, public HttpControllerBase
{ {
public: public:
static const bool isAutoCreation = AutoCreation; static constexpr bool isAutoCreation = AutoCreation;
protected: protected:
template <typename FUNCTION> template <typename FUNCTION>

View File

@ -69,11 +69,10 @@ struct is_printable : std::false_type
}; };
template <typename _Tp> template <typename _Tp>
struct is_printable<_Tp, struct is_printable<
typename std::enable_if< _Tp,
std::is_same<decltype(std::cout << std::declval<_Tp>()), std::enable_if_t<std::is_same_v<decltype(std::cout << std::declval<_Tp>()),
std::ostream &>::value>::type> std::ostream &>>> : std::true_type
: std::true_type
{ {
}; };
@ -167,10 +166,10 @@ inline std::string attemptPrint(T &&v)
// Poor man's if constexpr because SFINAE don't disambiguate between // Poor man's if constexpr because SFINAE don't disambiguate between
// possible resolutions // possible resolutions
return typename std::conditional< return
std::is_convertible<T, std::string_view>::value, typename std::conditional_t<std::is_convertible_v<T, std::string_view>,
internal::StringPrinter, internal::StringPrinter,
DefaultPrinter>::type()(v); DefaultPrinter>()(v);
} }
// Specializations to reduce template construction // Specializations to reduce template construction
@ -281,7 +280,7 @@ struct Lhs
template <typename RhsType> template <typename RhsType>
ComparsionResult operator&&(const RhsType &rhs) ComparsionResult operator&&(const RhsType &rhs)
{ {
static_assert(!std::is_same<RhsType, void>::value, static_assert(!std::is_same_v<RhsType, void>,
" && is not supported in expression decomposition"); " && is not supported in expression decomposition");
return {}; return {};
} }
@ -289,7 +288,7 @@ struct Lhs
template <typename RhsType> template <typename RhsType>
ComparsionResult operator||(const RhsType &rhs) ComparsionResult operator||(const RhsType &rhs)
{ {
static_assert(!std::is_same<RhsType, void>::value, static_assert(!std::is_same_v<RhsType, void>,
" || is not supported in expression decomposition"); " || is not supported in expression decomposition");
return {}; return {};
} }
@ -297,7 +296,7 @@ struct Lhs
template <typename RhsType> template <typename RhsType>
ComparsionResult operator|(const RhsType &rhs) ComparsionResult operator|(const RhsType &rhs)
{ {
static_assert(!std::is_same<RhsType, void>::value, static_assert(!std::is_same_v<RhsType, void>,
" | is not supported in expression decomposition"); " | is not supported in expression decomposition");
return {}; return {};
} }
@ -305,7 +304,7 @@ struct Lhs
template <typename RhsType> template <typename RhsType>
ComparsionResult operator&(const RhsType &rhs) ComparsionResult operator&(const RhsType &rhs)
{ {
static_assert(!std::is_same<RhsType, void>::value, static_assert(!std::is_same_v<RhsType, void>,
" & is not supported in expression decomposition"); " & is not supported in expression decomposition");
return {}; return {};
} }

View File

@ -106,8 +106,7 @@ class DROGON_EXPORT PluginBase : public virtual DrObjectBase,
template <typename T> template <typename T>
struct IsPlugin struct IsPlugin
{ {
using TYPE = using TYPE = std::remove_cv_t<typename std::remove_reference_t<T>>;
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
static int test(void *) static int test(void *)
{ {

View File

@ -50,7 +50,7 @@ struct FunctionTraits;
template <typename Function> template <typename Function>
struct FunctionTraits struct FunctionTraits
: public FunctionTraits< : public FunctionTraits<
decltype(&std::remove_reference<Function>::type::operator())> decltype(&std::remove_reference_t<Function>::operator())>
{ {
static const bool isClassFunction = false; static const bool isClassFunction = false;
static const bool isDrObjectClass = false; static const bool isDrObjectClass = false;
@ -190,7 +190,7 @@ struct FunctionTraits<ReturnType (*)(Arguments...)>
template <std::size_t Index> template <std::size_t Index>
using argument = using argument =
typename std::tuple_element<Index, std::tuple<Arguments...>>::type; typename std::tuple_element_t<Index, std::tuple<Arguments...>>;
static const std::size_t arity = sizeof...(Arguments); static const std::size_t arity = sizeof...(Arguments);
using class_type = void; using class_type = void;

View File

@ -37,8 +37,7 @@ struct CanConvertToString
static no test(...); static no test(...);
public: public:
static constexpr bool value = static constexpr bool value = std::is_same_v<decltype(test<Type>(0)), yes>;
std::is_same<decltype(test<Type>(0)), yes>::value;
}; };
} // namespace internal } // namespace internal
@ -53,21 +52,20 @@ class OStringStream
} }
template <typename T> template <typename T>
std::enable_if_t<!internal::CanConvertToString<T>::value, OStringStream &> OStringStream &operator<<(T &&value)
operator<<(T &&value)
{ {
std::stringstream ss; if constexpr (internal::CanConvertToString<T>::value)
ss << std::forward<T>(value); {
buffer_.append(ss.str()); buffer_.append(std::to_string(std::forward<T>(value)));
return *this; return *this;
} }
else
template <typename T> {
std::enable_if_t<internal::CanConvertToString<T>::value, OStringStream &> std::stringstream ss;
operator<<(T &&value) ss << std::forward<T>(value);
{ buffer_.append(ss.str());
buffer_.append(std::to_string(std::forward<T>(value))); return *this;
return *this; }
} }
template <int N> template <int N>

View File

@ -53,9 +53,43 @@ struct CanConvertFromStringStream
public: public:
static constexpr bool value = static constexpr bool value =
std::is_same<decltype(test<T>(nullptr, std::stringstream())), std::is_same_v<decltype(test<T>(nullptr, std::stringstream())), yes>;
yes>::value;
}; };
template <typename T>
struct CanConstructFromString
{
private:
using yes = std::true_type;
using no = std::false_type;
template <typename U>
static auto test(U *p) -> decltype(U(std::string{}), yes());
template <typename>
static no test(...);
public:
static constexpr bool value =
std::is_same_v<decltype(test<T>(nullptr)), yes>;
};
template <typename T>
struct CanConvertFromString
{
private:
using yes = std::true_type;
using no = std::false_type;
template <class U>
static auto test(U *p) -> decltype(*p = std::string(), yes());
template <class>
static no test(...);
public:
static constexpr bool value =
std::is_same_v<decltype(test<T>(nullptr)), yes>;
};
} // namespace internal } // namespace internal
namespace utils namespace utils
@ -79,7 +113,8 @@ DROGON_EXPORT std::string genRandomString(int length);
/// Convert a binary string to hex format /// Convert a binary string to hex format
DROGON_EXPORT std::string binaryStringToHex(const unsigned char *ptr, DROGON_EXPORT std::string binaryStringToHex(const unsigned char *ptr,
size_t length); size_t length,
bool lowerCase = false);
/// Get a binary string from hexadecimal format /// Get a binary string from hexadecimal format
DROGON_EXPORT std::string hexToBinaryString(const char *ptr, size_t length); DROGON_EXPORT std::string hexToBinaryString(const char *ptr, size_t length);
@ -88,6 +123,11 @@ DROGON_EXPORT std::string hexToBinaryString(const char *ptr, size_t length);
DROGON_EXPORT std::vector<char> hexToBinaryVector(const char *ptr, DROGON_EXPORT std::vector<char> hexToBinaryVector(const char *ptr,
size_t length); size_t length);
DROGON_EXPORT void binaryStringToHex(const char *ptr,
size_t length,
char *out,
bool lowerCase = false);
/// Split the string into multiple separated strings. /// Split the string into multiple separated strings.
/** /**
* @param acceptEmptyString if true, empty strings are accepted in the * @param acceptEmptyString if true, empty strings are accepted in the
@ -106,7 +146,7 @@ DROGON_EXPORT std::set<std::string> splitStringToSet(
const std::string &separator); const std::string &separator);
/// Get UUID string. /// Get UUID string.
DROGON_EXPORT std::string getUuid(); DROGON_EXPORT std::string getUuid(bool lowercase = true);
/// Get the encoded length of base64. /// Get the encoded length of base64.
constexpr size_t base64EncodedLength(size_t in_len, bool padded = true) constexpr size_t base64EncodedLength(size_t in_len, bool padded = true)
@ -396,24 +436,22 @@ DROGON_EXPORT bool secureRandomBytes(void *ptr, size_t size);
DROGON_EXPORT std::string secureRandomString(size_t size); DROGON_EXPORT std::string secureRandomString(size_t size);
template <typename T> template <typename T>
typename std::enable_if<internal::CanConvertFromStringStream<T>::value, T>::type T fromString(const std::string &p) noexcept(false)
fromString(const std::string &p) noexcept(false)
{ {
T value{}; if constexpr (internal::CanConvertFromStringStream<T>::value)
if (!p.empty())
{ {
std::stringstream ss(p); T value{};
ss >> value; if (!p.empty())
{
std::stringstream ss(p);
ss >> value;
}
return value;
}
else
{
throw std::runtime_error("Bad type conversion");
} }
return value;
}
template <typename T>
typename std::enable_if<!(internal::CanConvertFromStringStream<T>::value),
T>::type
fromString(const std::string &) noexcept(false)
{
throw std::runtime_error("Bad type conversion");
} }
template <> template <>

View File

@ -1375,7 +1375,6 @@ void Http2Transport::sendFrame(const internal::H2Frame &frame, int32_t streamId)
void Http2Transport::sendBufferedData() void Http2Transport::sendBufferedData()
{ {
LOG_WARN << "Sending buffered data. Size: " << batchedSendBuffer.size();
if (batchedSendBuffer.size() == 0) if (batchedSendBuffer.size() == 0)
return; return;
OByteStream buffer; OByteStream buffer;

View File

@ -777,14 +777,14 @@ void HttpAppFrameworkImpl::findSessionForRequest(const HttpRequestImplPtr &req)
if (useSession_) if (useSession_)
{ {
std::string sessionId = req->getCookie(sessionCookieKey_); std::string sessionId = req->getCookie(sessionCookieKey_);
bool needSetJsessionid = false; bool needSetSessionid = false;
if (sessionId.empty()) if (sessionId.empty())
{ {
sessionId = utils::getUuid(); sessionId = sessionIdGeneratorCallback_();
needSetJsessionid = true; needSetSessionid = true;
} }
req->setSession( req->setSession(
sessionManagerPtr_->getSession(sessionId, needSetJsessionid)); sessionManagerPtr_->getSession(sessionId, needSetSessionid));
} }
} }

View File

@ -25,6 +25,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "SessionManager.h" #include "SessionManager.h"
#include "drogon/utils/Utilities.h"
#include "impl_forwards.h" #include "impl_forwards.h"
namespace trantor namespace trantor
@ -250,7 +251,9 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
HttpAppFramework &setSessionIdGenerator( HttpAppFramework &setSessionIdGenerator(
SessionManager::IdGeneratorCallback idGeneratorCallback = nullptr) SessionManager::IdGeneratorCallback idGeneratorCallback = nullptr)
{ {
sessionIdGeneratorCallback_ = idGeneratorCallback; sessionIdGeneratorCallback_ =
idGeneratorCallback ? idGeneratorCallback
: []() { return utils::getUuid(true); };
return *this; return *this;
} }

View File

@ -760,7 +760,7 @@ void HttpRequestImpl::appendToBody(const char *data, size_t length)
void HttpRequestImpl::createTmpFile() void HttpRequestImpl::createTmpFile()
{ {
auto tmpfile = HttpAppFrameworkImpl::instance().getUploadPath(); auto tmpfile = HttpAppFrameworkImpl::instance().getUploadPath();
auto fileName = utils::getUuid(); auto fileName = utils::getUuid(false);
tmpfile.append("/tmp/") tmpfile.append("/tmp/")
.append(1, fileName[0]) .append(1, fileName[0])
.append(1, fileName[1]) .append(1, fileName[1])

View File

@ -13,7 +13,6 @@
*/ */
#include "SessionManager.h" #include "SessionManager.h"
#include <drogon/utils/Utilities.h>
using namespace drogon; using namespace drogon;
@ -27,8 +26,7 @@ SessionManager::SessionManager(
timeout_(timeout), timeout_(timeout),
sessionStartAdvices_(startAdvices), sessionStartAdvices_(startAdvices),
sessionDestroyAdvices_(destroyAdvices), sessionDestroyAdvices_(destroyAdvices),
idGeneratorCallback_(idGeneratorCallback ? idGeneratorCallback idGeneratorCallback_(idGeneratorCallback)
: utils::getUuid)
{ {
if (timeout_ > 0) if (timeout_ > 0)
{ {

View File

@ -37,7 +37,7 @@ class SessionManager : public trantor::NonCopyable
size_t timeout, size_t timeout,
const std::vector<AdviceStartSessionCallback> &startAdvices, const std::vector<AdviceStartSessionCallback> &startAdvices,
const std::vector<AdviceDestroySessionCallback> &destroyAdvices, const std::vector<AdviceDestroySessionCallback> &destroyAdvices,
IdGeneratorCallback idGeneratorCallback = nullptr); IdGeneratorCallback idGeneratorCallback);
~SessionManager() ~SessionManager()
{ {

View File

@ -289,31 +289,55 @@ std::string hexToBinaryString(const char *ptr, size_t length)
return ret; return ret;
} }
std::string binaryStringToHex(const unsigned char *ptr, size_t length) DROGON_EXPORT void binaryStringToHex(const char *ptr,
size_t length,
char *out,
bool lowerCase)
{ {
std::string idString;
for (size_t i = 0; i < length; ++i) for (size_t i = 0; i < length; ++i)
{ {
int value = (ptr[i] & 0xf0) >> 4; int value = (ptr[i] & 0xf0) >> 4;
if (value < 10) if (value < 10)
{ {
idString.append(1, char(value + 48)); out[i * 2] = char(value + 48);
} }
else else
{ {
idString.append(1, char(value + 55)); if (!lowerCase)
{
out[i * 2] = char(value + 55);
}
else
{
out[i * 2] = char(value + 87);
}
} }
value = (ptr[i] & 0x0f); value = (ptr[i] & 0x0f);
if (value < 10) if (value < 10)
{ {
idString.append(1, char(value + 48)); out[i * 2 + 1] = char(value + 48);
} }
else else
{ {
idString.append(1, char(value + 55)); if (!lowerCase)
{
out[i * 2 + 1] = char(value + 55);
}
else
{
out[i * 2 + 1] = char(value + 87);
}
} }
} }
}
std::string binaryStringToHex(const unsigned char *ptr,
size_t length,
bool lowercase)
{
std::string idString(length * 2, '\0');
binaryStringToHex((const char *)ptr, length, &idString[0], lowercase);
return idString; return idString;
} }
@ -342,7 +366,23 @@ std::set<std::string> splitStringToSet(const std::string &str,
return ret; return ret;
} }
std::string getUuid() inline std::string createUuidString(const char *str, size_t len, bool lowercase)
{
assert(len == 16);
std::string uuid(36, '\0');
binaryStringToHex(str, 4, &uuid[0], lowercase);
uuid[8] = '-';
binaryStringToHex(str + 4, 2, &uuid[9], lowercase);
uuid[13] = '-';
binaryStringToHex(str + 6, 2, &uuid[14], lowercase);
uuid[18] = '-';
binaryStringToHex(str + 8, 2, &uuid[19], lowercase);
uuid[23] = '-';
binaryStringToHex(str + 10, 6, &uuid[24], lowercase);
return uuid;
}
std::string getUuid(bool lowercase)
{ {
#if USE_OSSP_UUID #if USE_OSSP_UUID
uuid_t *uuid; uuid_t *uuid;
@ -352,7 +392,7 @@ std::string getUuid()
size_t len{0}; size_t len{0};
uuid_export(uuid, UUID_FMT_BIN, &str, &len); uuid_export(uuid, UUID_FMT_BIN, &str, &len);
uuid_destroy(uuid); uuid_destroy(uuid);
std::string ret{binaryStringToHex((const unsigned char *)str, len)}; auto ret = createUuidString(str, len, lowercase);
free(str); free(str);
return ret; return ret;
#elif defined __FreeBSD__ || defined __OpenBSD__ #elif defined __FreeBSD__ || defined __OpenBSD__
@ -370,7 +410,7 @@ std::string getUuid()
uuid_enc_be(binstr, uuid); uuid_enc_be(binstr, uuid);
#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */ #endif /* _BYTE_ORDER == _LITTLE_ENDIAN */
delete uuid; delete uuid;
std::string ret{binaryStringToHex((const unsigned char *)binstr, 16)}; auto ret = createUuidString(binstr, 16, lowercase);
free(binstr); free(binstr);
return ret; return ret;
#elif defined _WIN32 #elif defined _WIN32
@ -379,7 +419,7 @@ std::string getUuid()
char tempStr[100]; char tempStr[100];
auto len = snprintf(tempStr, auto len = snprintf(tempStr,
sizeof(tempStr), sizeof(tempStr),
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uu.Data1, uu.Data1,
uu.Data2, uu.Data2,
uu.Data3, uu.Data3,
@ -395,7 +435,8 @@ std::string getUuid()
#else #else
uuid_t uu; uuid_t uu;
uuid_generate(uu); uuid_generate(uu);
return binaryStringToHex(uu, 16); auto uuid = createUuidString((const char *)uu, 16, lowercase);
return uuid;
#endif #endif
} }

View File

@ -25,7 +25,9 @@ set(UNITTEST_SOURCES
unittests/StringOpsTest.cc unittests/StringOpsTest.cc
unittests/ControllerCreationTest.cc unittests/ControllerCreationTest.cc
unittests/MultiPartParserTest.cc unittests/MultiPartParserTest.cc
unittests/SlashRemoverTest.cc) unittests/SlashRemoverTest.cc
unittests/UuidUnittest.cc
)
if(DROGON_CXX_STANDARD GREATER_EQUAL 20 AND HAS_COROUTINE) if(DROGON_CXX_STANDARD GREATER_EQUAL 20 AND HAS_COROUTINE)
set(UNITTEST_SOURCES ${UNITTEST_SOURCES} unittests/CoroutineTest.cc) set(UNITTEST_SOURCES ${UNITTEST_SOURCES} unittests/CoroutineTest.cc)

View File

@ -0,0 +1,17 @@
#include <string_view>
#include <drogon/utils/Utilities.h>
#include <iostream>
#include <drogon/drogon_test.h>
DROGON_TEST(UuidTest)
{
auto uuid = drogon::utils::getUuid();
std::cout << "uuid: " << uuid << std::endl;
CHECK(uuid[8] == '-');
CHECK(uuid[13] == '-');
CHECK(uuid[18] == '-');
CHECK(uuid[23] == '-');
CHECK(uuid.size() == 36);
}

View File

@ -167,7 +167,6 @@ class BaseBuilder
} }
public: public:
#ifdef __cpp_if_constexpr
static ResultType convert_result(const Result &r) static ResultType convert_result(const Result &r)
{ {
if constexpr (SelectAll) if constexpr (SelectAll)
@ -198,48 +197,6 @@ class BaseBuilder
} }
} }
} }
#else
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<SA, std::nullptr_t> = nullptr,
std::enable_if_t<SI, std::nullptr_t> = nullptr>
static inline T convert_result(const Result &r)
{
return T(r[0]);
}
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<SA, std::nullptr_t> = nullptr,
std::enable_if_t<!SI, std::nullptr_t> = nullptr>
static inline std::vector<T> convert_result(const Result &r)
{
std::vector<T> ret;
for (const Row &row : r)
{
ret.template emplace_back(T(row));
}
return ret;
}
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<!SA, std::nullptr_t> = nullptr,
std::enable_if_t<SI, std::nullptr_t> = nullptr>
static inline Row convert_result(const Result &r)
{
return r[0];
}
template <bool SA = SelectAll,
bool SI = Single,
std::enable_if_t<!SA, std::nullptr_t> = nullptr,
std::enable_if_t<!SI, std::nullptr_t> = nullptr>
static inline Result convert_result(const Result &r)
{
return r;
}
#endif
inline ResultType execSync(const DbClientPtr &client) inline ResultType execSync(const DbClientPtr &client)
{ {

View File

@ -80,13 +80,12 @@ class CoroMapper : public Mapper<T>
inline internal::MapperAwaiter<T> findByPrimaryKey(const TraitsPKType &key) inline internal::MapperAwaiter<T> findByPrimaryKey(const TraitsPKType &key)
{ {
if constexpr (!std::is_same<typename T::PrimaryKeyType, void>::value) if constexpr (!std::is_same_v<typename T::PrimaryKeyType, void>)
{ {
auto lb = [this, key](SingleRowCallback &&callback, auto lb = [this, key](SingleRowCallback &&callback,
ExceptPtrCallback &&errCallback) mutable { ExceptPtrCallback &&errCallback) mutable {
static_assert( static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
!std::is_same<typename T::PrimaryKeyType, void>::value, "No primary key in the table!");
"No primary key in the table!");
static_assert( static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value, internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, " "No function member named sqlForFindingByPrimaryKey, "
@ -417,9 +416,8 @@ class CoroMapper : public Mapper<T>
auto lb = [this, obj](CountCallback &&callback, auto lb = [this, obj](CountCallback &&callback,
ExceptPtrCallback &&errCallback) { ExceptPtrCallback &&errCallback) {
this->clear(); this->clear();
static_assert( static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
!std::is_same<typename T::PrimaryKeyType, void>::value, "No primary key in the table!");
"No primary key in the table!");
std::string sql = "update "; std::string sql = "update ";
sql += T::tableName; sql += T::tableName;
sql += " set "; sql += " set ";
@ -530,9 +528,8 @@ class CoroMapper : public Mapper<T>
auto lb = [this, obj](CountCallback &&callback, auto lb = [this, obj](CountCallback &&callback,
ExceptPtrCallback &&errCallback) { ExceptPtrCallback &&errCallback) {
this->clear(); this->clear();
static_assert( static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
!std::is_same<typename T::PrimaryKeyType, void>::value, "No primary key in the table!");
"No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
sql += " "; sql += " ";
@ -555,9 +552,8 @@ class CoroMapper : public Mapper<T>
auto lb = [this, criteria](CountCallback &&callback, auto lb = [this, criteria](CountCallback &&callback,
ExceptPtrCallback &&errCallback) { ExceptPtrCallback &&errCallback) {
this->clear(); this->clear();
static_assert( static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
!std::is_same<typename T::PrimaryKeyType, void>::value, "No primary key in the table!");
"No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
@ -584,7 +580,7 @@ class CoroMapper : public Mapper<T>
inline internal::MapperAwaiter<size_t> deleteByPrimaryKey( inline internal::MapperAwaiter<size_t> deleteByPrimaryKey(
const TraitsPKType &key) const TraitsPKType &key)
{ {
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
static_assert( static_assert(
internal::has_sqlForDeletingByPrimaryKey<T>::value, internal::has_sqlForDeletingByPrimaryKey<T>::value,

View File

@ -110,9 +110,10 @@ struct FunctionTraits<ReturnType (*)(Arguments...)>
static const std::size_t arity = sizeof...(Arguments); static const std::size_t arity = sizeof...(Arguments);
// static const bool isSqlCallback = false;
static const bool isSqlCallback = true; static const bool isSqlCallback = true;
static const bool isStepResultCallback = true; static const bool isStepResultCallback = true;
static const bool isExceptCallback = false;
static const bool isPtr = false;
}; };
} // namespace internal } // namespace internal
} // namespace orm } // namespace orm

View File

@ -62,8 +62,7 @@ struct has_sqlForFindingByPrimaryKey
static no test(...); static no test(...);
public: public:
static constexpr bool value = static constexpr bool value = std::is_same_v<decltype(test<T>(0)), yes>;
std::is_same<decltype(test<T>(0)), yes>::value;
}; };
template <typename T> template <typename T>
@ -81,8 +80,7 @@ struct has_sqlForDeletingByPrimaryKey
static no test(...); static no test(...);
public: public:
static constexpr bool value = static constexpr bool value = std::is_same_v<decltype(test<T>(0)), yes>;
std::is_same<decltype(test<T>(0)), yes>::value;
}; };
} // namespace internal } // namespace internal
@ -185,7 +183,7 @@ class Mapper
using CountCallback = std::function<void(const size_t)>; using CountCallback = std::function<void(const size_t)>;
using TraitsPKType = typename internal:: using TraitsPKType = typename internal::
Traits<T, !std::is_same<typename T::PrimaryKeyType, void>::value>::type; Traits<T, !std::is_same_v<typename T::PrimaryKeyType, void>>::type;
/** /**
* @brief Find a record by the primary key. * @brief Find a record by the primary key.
@ -196,53 +194,48 @@ class Mapper
*/ */
template <typename U = T> template <typename U = T>
inline typename std::enable_if< inline T findByPrimaryKey(const TraitsPKType &key) noexcept(false)
!std::is_same<typename U::PrimaryKeyType, void>::value,
T>::type
findByPrimaryKey(const TraitsPKType &key) noexcept(false)
{ {
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)
"No primary key in the table!");
static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
// return findOne(Criteria(T::primaryKeyName, key));
std::string sql = T::sqlForFindingByPrimaryKey();
if (forUpdate_)
{ {
sql += " for update"; static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!");
static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
// return findOne(Criteria(T::primaryKeyName, key));
std::string sql = T::sqlForFindingByPrimaryKey();
if (forUpdate_)
{
sql += " for update";
}
clear();
Result r(nullptr);
{
auto binder = *client_ << std::move(sql);
outputPrimeryKeyToBinder(key, binder);
binder << Mode::Blocking;
binder >> [&r](const Result &result) { r = result; };
binder.exec(); // exec may be throw exception;
}
if (r.size() == 0)
{
throw UnexpectedRows("0 rows found");
}
else if (r.size() > 1)
{
throw UnexpectedRows("Found more than one row");
}
auto row = r[0];
return T(row);
} }
clear(); else
Result r(nullptr);
{ {
auto binder = *client_ << std::move(sql); LOG_FATAL << "The table must have a primary key";
outputPrimeryKeyToBinder(key, binder); abort();
binder << Mode::Blocking;
binder >> [&r](const Result &result) { r = result; };
binder.exec(); // exec may be throw exception;
} }
if (r.size() == 0)
{
throw UnexpectedRows("0 rows found");
}
else if (r.size() > 1)
{
throw UnexpectedRows("Found more than one row");
}
auto row = r[0];
return T(row);
}
template <typename U = T>
inline typename std::enable_if<
std::is_same<typename U::PrimaryKeyType, void>::value,
T>::type
findByPrimaryKey(const TraitsPKType &key) noexcept(false)
{
LOG_FATAL << "The table must have a primary key";
abort();
} }
/** /**
@ -253,56 +246,49 @@ class Mapper
* @param ecb Is called when an error occurs or a record cannot be found. * @param ecb Is called when an error occurs or a record cannot be found.
*/ */
template <typename U = T> template <typename U = T>
inline typename std::enable_if< inline void findByPrimaryKey(const TraitsPKType &key,
!std::is_same<typename U::PrimaryKeyType, void>::value, const SingleRowCallback &rcb,
void>::type const ExceptionCallback &ecb) noexcept
findByPrimaryKey(const TraitsPKType &key,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept
{ {
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)
"No primary key in the table!");
static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
// findOne(Criteria(T::primaryKeyName, key), rcb, ecb);
std::string sql = T::sqlForFindingByPrimaryKey();
if (forUpdate_)
{ {
sql += " for update"; static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!");
static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
// findOne(Criteria(T::primaryKeyName, key), rcb, ecb);
std::string sql = T::sqlForFindingByPrimaryKey();
if (forUpdate_)
{
sql += " for update";
}
clear();
auto binder = *client_ << std::move(sql);
outputPrimeryKeyToBinder(key, binder);
binder >> [ecb, rcb](const Result &r) {
if (r.size() == 0)
{
ecb(UnexpectedRows("0 rows found"));
}
else if (r.size() > 1)
{
ecb(UnexpectedRows("Found more than one row"));
}
else
{
rcb(T(r[0]));
}
};
binder >> ecb;
}
else
{
LOG_FATAL << "The table must have a primary key";
abort();
} }
clear();
auto binder = *client_ << std::move(sql);
outputPrimeryKeyToBinder(key, binder);
binder >> [ecb, rcb](const Result &r) {
if (r.size() == 0)
{
ecb(UnexpectedRows("0 rows found"));
}
else if (r.size() > 1)
{
ecb(UnexpectedRows("Found more than one row"));
}
else
{
rcb(T(r[0]));
}
};
binder >> ecb;
}
template <typename U = T>
inline typename std::enable_if<
std::is_same<typename U::PrimaryKeyType, void>::value,
void>::type
findByPrimaryKey(const TraitsPKType &key,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
LOG_FATAL << "The table must have a primary key";
abort();
} }
/** /**
@ -315,60 +301,56 @@ class Mapper
* user calls the get() method of the future object. * user calls the get() method of the future object.
*/ */
template <typename U = T> template <typename U = T>
inline typename std::enable_if< inline std::future<T> findFutureByPrimaryKey(
!std::is_same<typename U::PrimaryKeyType, void>::value, const TraitsPKType &key) noexcept
std::future<T>>::type
findFutureByPrimaryKey(const TraitsPKType &key) noexcept
{ {
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)
"No primary key in the table!");
static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
// return findFutureOne(Criteria(T::primaryKeyName, key));
std::string sql = T::sqlForFindingByPrimaryKey();
if (forUpdate_)
{ {
sql += " for update"; static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!");
static_assert(
internal::has_sqlForFindingByPrimaryKey<T>::value,
"No function member named sqlForFindingByPrimaryKey, please "
"make sure that the model class is generated by the latest "
"version of drogon_ctl");
// return findFutureOne(Criteria(T::primaryKeyName, key));
std::string sql = T::sqlForFindingByPrimaryKey();
if (forUpdate_)
{
sql += " for update";
}
clear();
auto binder = *client_ << std::move(sql);
outputPrimeryKeyToBinder(key, binder);
std::shared_ptr<std::promise<T>> prom =
std::make_shared<std::promise<T>>();
binder >> [prom](const Result &r) {
if (r.size() == 0)
{
prom->set_exception(std::make_exception_ptr(
UnexpectedRows("0 rows found")));
}
else if (r.size() > 1)
{
prom->set_exception(std::make_exception_ptr(
UnexpectedRows("Found more than one row")));
}
else
{
prom->set_value(T(r[0]));
}
};
binder >>
[prom](const std::exception_ptr &e) { prom->set_exception(e); };
binder.exec();
return prom->get_future();
}
else
{
LOG_FATAL << "The table must have a primary key";
abort();
} }
clear();
auto binder = *client_ << std::move(sql);
outputPrimeryKeyToBinder(key, binder);
std::shared_ptr<std::promise<T>> prom =
std::make_shared<std::promise<T>>();
binder >> [prom](const Result &r) {
if (r.size() == 0)
{
prom->set_exception(
std::make_exception_ptr(UnexpectedRows("0 rows found")));
}
else if (r.size() > 1)
{
prom->set_exception(std::make_exception_ptr(
UnexpectedRows("Found more than one row")));
}
else
{
prom->set_value(T(r[0]));
}
};
binder >>
[prom](const std::exception_ptr &e) { prom->set_exception(e); };
binder.exec();
return prom->get_future();
}
template <typename U = T>
inline typename std::enable_if<
std::is_same<typename U::PrimaryKeyType, void>::value,
std::future<U>>::type
findFutureByPrimaryKey(const TraitsPKType &key) noexcept
{
LOG_FATAL << "The table must have a primary key";
abort();
} }
/** /**
@ -701,67 +683,57 @@ class Mapper
} }
template <typename PKType = decltype(T::primaryKeyName)> template <typename PKType = decltype(T::primaryKeyName)>
typename std::enable_if<std::is_same<const std::string, PKType>::value, void makePrimaryKeyCriteria(std::string &sql)
void>::type
makePrimaryKeyCriteria(std::string &sql)
{ {
sql += " where "; if constexpr (std::is_same_v<const std::string, PKType>)
sql += T::primaryKeyName;
sql += " = $?";
}
template <typename PKType = decltype(T::primaryKeyName)>
typename std::enable_if<
std::is_same<const std::vector<std::string>, PKType>::value,
void>::type
makePrimaryKeyCriteria(std::string &sql)
{
sql += " where ";
for (size_t i = 0; i < T::primaryKeyName.size(); ++i)
{ {
sql += T::primaryKeyName[i]; sql += " where ";
sql += T::primaryKeyName;
sql += " = $?"; sql += " = $?";
if (i < (T::primaryKeyName.size() - 1)) }
else if constexpr (std::is_same_v<const std::vector<std::string>,
PKType>)
{
sql += " where ";
for (size_t i = 0; i < T::primaryKeyName.size(); ++i)
{ {
sql += " and "; sql += T::primaryKeyName[i];
sql += " = $?";
if (i < (T::primaryKeyName.size() - 1))
{
sql += " and ";
}
} }
} }
} }
template <typename PKType = decltype(T::primaryKeyName)> template <typename PKType = decltype(T::primaryKeyName)>
typename std::enable_if<std::is_same<const std::string, PKType>::value, void outputPrimeryKeyToBinder(const TraitsPKType &pk,
void>::type internal::SqlBinder &binder)
outputPrimeryKeyToBinder(const TraitsPKType &pk,
internal::SqlBinder &binder)
{ {
binder << pk; if constexpr (std::is_same_v<const std::string, PKType>)
} {
binder << pk;
template <typename PKType = decltype(T::primaryKeyName)> }
typename std::enable_if< else if constexpr (std::is_same_v<const std::vector<std::string>,
std::is_same<const std::vector<std::string>, PKType>::value, PKType>)
void>::type {
outputPrimeryKeyToBinder(const TraitsPKType &pk, tupleToBinder<typename T::PrimaryKeyType>(pk, binder);
internal::SqlBinder &binder) }
{
tupleToBinder<typename T::PrimaryKeyType>(pk, binder);
} }
template <typename TP, ssize_t N = std::tuple_size<TP>::value> template <typename TP, ssize_t N = std::tuple_size<TP>::value>
typename std::enable_if<(N > 1), void>::type tupleToBinder( void tupleToBinder(const TP &t, internal::SqlBinder &binder)
const TP &t,
internal::SqlBinder &binder)
{ {
tupleToBinder<TP, N - 1>(t, binder); if constexpr (N > 1)
binder << std::get<N - 1>(t); {
} tupleToBinder<TP, N - 1>(t, binder);
binder << std::get<N - 1>(t);
template <typename TP, ssize_t N = std::tuple_size<TP>::value> }
typename std::enable_if<(N == 1), void>::type tupleToBinder( else if constexpr (N == 1)
const TP &t, {
internal::SqlBinder &binder) binder << std::get<0>(t);
{ }
binder << std::get<0>(t);
} }
std::string replaceSqlPlaceHolder(const std::string &sqlStr, std::string replaceSqlPlaceHolder(const std::string &sqlStr,
@ -1321,7 +1293,7 @@ template <typename T>
inline size_t Mapper<T>::update(const T &obj) noexcept(false) inline size_t Mapper<T>::update(const T &obj) noexcept(false)
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "update "; std::string sql = "update ";
sql += T::tableName; sql += T::tableName;
@ -1394,7 +1366,7 @@ inline void Mapper<T>::update(const T &obj,
const ExceptionCallback &ecb) noexcept const ExceptionCallback &ecb) noexcept
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "update "; std::string sql = "update ";
sql += T::tableName; sql += T::tableName;
@ -1457,7 +1429,7 @@ template <typename T>
inline std::future<size_t> Mapper<T>::updateFuture(const T &obj) noexcept inline std::future<size_t> Mapper<T>::updateFuture(const T &obj) noexcept
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "update "; std::string sql = "update ";
sql += T::tableName; sql += T::tableName;
@ -1529,7 +1501,7 @@ template <typename T>
inline size_t Mapper<T>::deleteOne(const T &obj) noexcept(false) inline size_t Mapper<T>::deleteOne(const T &obj) noexcept(false)
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
@ -1556,7 +1528,7 @@ inline void Mapper<T>::deleteOne(const T &obj,
const ExceptionCallback &ecb) noexcept const ExceptionCallback &ecb) noexcept
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
@ -1575,7 +1547,7 @@ template <typename T>
inline std::future<size_t> Mapper<T>::deleteFutureOne(const T &obj) noexcept inline std::future<size_t> Mapper<T>::deleteFutureOne(const T &obj) noexcept
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
@ -1599,7 +1571,7 @@ template <typename T>
inline size_t Mapper<T>::deleteBy(const Criteria &criteria) noexcept(false) inline size_t Mapper<T>::deleteBy(const Criteria &criteria) noexcept(false)
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
@ -1631,7 +1603,7 @@ inline void Mapper<T>::deleteBy(const Criteria &criteria,
const ExceptionCallback &ecb) noexcept const ExceptionCallback &ecb) noexcept
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
@ -1657,7 +1629,7 @@ inline std::future<size_t> Mapper<T>::deleteFutureBy(
const Criteria &criteria) noexcept const Criteria &criteria) noexcept
{ {
clear(); clear();
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
std::string sql = "delete from "; std::string sql = "delete from ";
sql += T::tableName; sql += T::tableName;
@ -1797,7 +1769,7 @@ template <typename T>
inline size_t Mapper<T>::deleteByPrimaryKey( inline size_t Mapper<T>::deleteByPrimaryKey(
const typename Mapper<T>::TraitsPKType &key) noexcept(false) const typename Mapper<T>::TraitsPKType &key) noexcept(false)
{ {
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value, static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,
"No function member named sqlForDeletingByPrimaryKey, please " "No function member named sqlForDeletingByPrimaryKey, please "
@ -1821,7 +1793,7 @@ inline void Mapper<T>::deleteByPrimaryKey(
const CountCallback &rcb, const CountCallback &rcb,
const ExceptionCallback &ecb) noexcept const ExceptionCallback &ecb) noexcept
{ {
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value, static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,
"No function member named sqlForDeletingByPrimaryKey, please " "No function member named sqlForDeletingByPrimaryKey, please "
@ -1839,7 +1811,7 @@ template <typename T>
inline std::future<size_t> Mapper<T>::deleteFutureByPrimaryKey( inline std::future<size_t> Mapper<T>::deleteFutureByPrimaryKey(
const typename Mapper<T>::TraitsPKType &key) noexcept const typename Mapper<T>::TraitsPKType &key) noexcept
{ {
static_assert(!std::is_same<typename T::PrimaryKeyType, void>::value, static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
"No primary key in the table!"); "No primary key in the table!");
static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value, static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,
"No function member named sqlForDeletingByPrimaryKey, please " "No function member named sqlForDeletingByPrimaryKey, please "

View File

@ -203,83 +203,78 @@ class CallbackHolder : public CallbackHolderBase
static const size_t argumentCount = traits::arity; static const size_t argumentCount = traits::arity;
template <bool isStep = traits::isStepResultCallback> template <bool isStep = traits::isStepResultCallback>
typename std::enable_if<isStep, void>::type run(const Result &result) void run(const Result &result)
{ {
if (result.empty()) if constexpr (isStep)
{ {
if (result.empty())
{
run(nullptr, true);
return;
}
for (auto const &row : result)
{
run(&row, false);
}
run(nullptr, true); run(nullptr, true);
return;
} }
for (auto const &row : result) else
{ {
run(&row, false); static_assert(argumentCount == 0,
"Your sql callback function type is wrong!");
function_(result);
} }
run(nullptr, true);
}
template <bool isStep = traits::isStepResultCallback>
typename std::enable_if<!isStep, void>::type run(const Result &result)
{
static_assert(argumentCount == 0,
"Your sql callback function type is wrong!");
function_(result);
} }
template <typename... Values, std::size_t Boundary = argumentCount> template <typename... Values, std::size_t Boundary = argumentCount>
typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run( void run(const Row *const row, bool isNull, Values &&...values)
const Row *const row,
bool isNull,
Values &&...values)
{ {
// call this function recursively until parameter's count equals to the if constexpr (sizeof...(Values) < Boundary)
// count of target function parameters
static_assert(
CallbackArgTypeTraits<NthArgumentType<sizeof...(Values)>>::isValid,
"your sql callback function argument type must be value "
"type or "
"const "
"left-reference type");
using ValueType =
typename std::remove_cv<typename std::remove_reference<
NthArgumentType<sizeof...(Values)>>::type>::type;
ValueType value = ValueType();
if (row && row->size() > sizeof...(Values))
{ {
// if(!VectorTypeTraits<ValueType>::isVector) // call this function recursively until parameter's count equals to
// value = (*row)[sizeof...(Values)].as<ValueType>(); // the count of target function parameters
// else static_assert(
// ; // value = CallbackArgTypeTraits<
// (*row)[sizeof...(Values)].asArray<VectorTypeTraits<ValueType>::ItemsType>(); NthArgumentType<sizeof...(Values)>>::isValid,
value = "your sql callback function argument type must be value "
makeValue<ValueType>((*row)[(Row::SizeType)sizeof...(Values)]); "type or "
"const "
"left-reference type");
using ValueType =
typename std::remove_cv<typename std::remove_reference<
NthArgumentType<sizeof...(Values)>>::type>::type;
ValueType value = ValueType();
if (row && row->size() > sizeof...(Values))
{
// if(!VectorTypeTraits<ValueType>::isVector)
// value = (*row)[sizeof...(Values)].as<ValueType>();
// else
// ; // value =
// (*row)[sizeof...(Values)].asArray<VectorTypeTraits<ValueType>::ItemsType>();
value = makeValue<ValueType>(
(*row)[(Row::SizeType)sizeof...(Values)]);
}
run(row, isNull, std::forward<Values>(values)..., std::move(value));
}
else if constexpr (sizeof...(Values) == Boundary)
{
function_(isNull, std::move(values)...);
} }
run(row, isNull, std::forward<Values>(values)..., std::move(value));
}
template <typename... Values, std::size_t Boundary = argumentCount>
typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run(
const Row *const,
bool isNull,
Values &&...values)
{
function_(isNull, std::move(values)...);
} }
template <typename ValueType> template <typename ValueType>
typename std::enable_if<VectorTypeTraits<ValueType>::isVector, ValueType makeValue(const Field &field)
ValueType>::type
makeValue(const Field &field)
{ {
return field.asArray<typename VectorTypeTraits<ValueType>::ItemsType>(); if constexpr (VectorTypeTraits<ValueType>::isVector)
} {
return field
template <typename ValueType> .asArray<typename VectorTypeTraits<ValueType>::ItemsType>();
typename std::enable_if<!VectorTypeTraits<ValueType>::isVector, }
ValueType>::type else
makeValue(const Field &field) {
{ return field.as<ValueType>();
return field.as<ValueType>(); }
} }
}; };
@ -346,51 +341,38 @@ class DROGON_EXPORT SqlBinder : public trantor::NonCopyable
template <typename CallbackType, template <typename CallbackType,
typename traits = typename traits =
FunctionTraits<typename std::decay<CallbackType>::type>> FunctionTraits<typename std::decay<CallbackType>::type>>
typename std::enable_if<traits::isExceptCallback && traits::isPtr, self &operator>>(CallbackType &&callback)
self>::type &
operator>>(CallbackType &&callback)
{ {
// LOG_DEBUG << "ptr callback"; if constexpr (traits::isExceptCallback)
isExceptionPtr_ = true; {
exceptionPtrCallback_ = std::forward<CallbackType>(callback); if constexpr (traits::isPtr)
return *this; {
} // LOG_DEBUG << "ptr callback";
isExceptionPtr_ = true;
template <typename CallbackType, exceptionPtrCallback_ = std::forward<CallbackType>(callback);
typename traits = return *this;
FunctionTraits<typename std::decay<CallbackType>::type>> }
typename std::enable_if<traits::isExceptCallback && !traits::isPtr, else
self>::type & {
operator>>(CallbackType &&callback) isExceptionPtr_ = false;
{ exceptionCallback_ = std::forward<CallbackType>(callback);
isExceptionPtr_ = false; return *this;
exceptionCallback_ = std::forward<CallbackType>(callback); }
return *this; }
} else if constexpr (traits::isSqlCallback)
{
template <typename CallbackType, callbackHolder_ = std::shared_ptr<CallbackHolderBase>(
typename traits = new CallbackHolder<typename std::decay<CallbackType>::type>(
FunctionTraits<typename std::decay<CallbackType>::type>> std::forward<CallbackType>(callback)));
typename std::enable_if<traits::isSqlCallback, self>::type &operator>>( return *this;
CallbackType &&callback) }
{
callbackHolder_ = std::shared_ptr<CallbackHolderBase>(
new CallbackHolder<typename std::decay<CallbackType>::type>(
std::forward<CallbackType>(callback)));
return *this;
} }
template <typename T> template <typename T>
typename std::enable_if< self &operator<<(T &&parameter)
!std::is_same<typename std::remove_cv<
typename std::remove_reference<T>::type>::type,
trantor::Date>::value,
self &>::type
operator<<(T &&parameter)
{ {
using ParaType = std::remove_cv_t<std::remove_reference_t<T>>;
++parametersNumber_; ++parametersNumber_;
using ParaType = typename std::remove_cv<
typename std::remove_reference<T>::type>::type;
std::shared_ptr<void> obj = std::make_shared<ParaType>(parameter); std::shared_ptr<void> obj = std::make_shared<ParaType>(parameter);
if (type_ == ClientType::PostgreSQL) if (type_ == ClientType::PostgreSQL)
{ {
@ -482,12 +464,7 @@ class DROGON_EXPORT SqlBinder : public trantor::NonCopyable
self &operator<<(std::string &&str); self &operator<<(std::string &&str);
self &operator<<(trantor::Date &&date) self &operator<<(trantor::Date date)
{
return operator<<(date.toDbStringLocal());
}
self &operator<<(const trantor::Date &date)
{ {
return operator<<(date.toDbStringLocal()); return operator<<(date.toDbStringLocal());
} }

@ -1 +1 @@
Subproject commit 624fc083076d7da88b1fb1dca1669ff48168a3ce Subproject commit d596221dd13a27ae86a637dba82154d0c5f96331