I am attempting to write a template specialization which serializes fields into a buffer. The below code MOSTLY works, although I am running into a weird problem when passing char pointer types to the code.
template <typename... Args>
struct ArgHandler;
// literal types
template <typename T, typename... Args>
struct ArgHandler<T, Args...>
{
static uint8_t* insert(uint8_t* buff, const T arg, const Args&... args)
{
auto data = reinterpret_cast<T*>(buff);
*data = static_cast<T>(arg);
return ArgHandler<Args...>::insert(buff + sizeof(T), args...);
}
};
// char ptr
template <typename... Args>
struct ArgHandler<const char*, Args...>
{
// strings
static uint8_t* insert(uint8_t* buff, const char* arg, const Args&... args)
{
auto* pos = reinterpret_cast<uint8_t*>(stpcpy(reinterpret_cast<char*>(buff), arg)) + 1;
return ArgHandler<Args...>::insert(pos, args...);
}
};
// char array
template <size_t N, typename... Args>
struct ArgHandler<char[N], Args...>
{
// strings
static uint8_t* insert(uint8_t* buff, const char (&arg)[N], const Args&... args)
{
auto* pos = reinterpret_cast<uint8_t*>(stpcpy(reinterpret_cast<char*>(buff), &arg[0])) + 1;
return ArgHandler<Args...>::insert(pos, args...);
}
};
// after last arg, end recursion
template <>
struct ArgHandler<>
{
static uint8_t* insert(uint8_t* buff)
{
return buff;
}
};
template <typename... Args>
void insertArgs(uint8_t* buff, const Args&... args)
{
ArgHandler<Args...>::insert(buff, args...);
}
If I create a char ptr on the stack of a function and then pass it to my buffer-insertion code, everything works. However, in the same code if I pass the argument of the calling function directly into the buffer-insertion function, the compiler resolves to the wrong specialization (specifically the "literal types" specialization).
// works
void someFunction(const char* str)
{
uint8_t buff[1024];
const char* local_str = str;
insertArgs(buff, local_str);
}
// resolves to incorrect specialization
void someFunction(const char* str)
{
uint8_t buff[1024];
insertArgs(buff, str);
}
I suspect this has to do with my lack of understanding of lvalue, rvalue, etc. Any help is appreciated!
Related
Imagine I have two functions:
void string(const char *str)
{
std::cout << "this is string" << std::endl;
}
void number(const char *str, double f)
{
std::cout << "this is number" << std::endl;
}
I want to write a generic wrapper so that be able to call format() like this:
int main() {
format("single arg");
format("format string", 1.0);
format("single arg", "format string", 1.0);
format("format string 1", 1.0, "just string arg", "format string 2", 2.0);
return 0;
}
That is if arguments come in pair {string, number}, then invoke number(); otherwise call string(). Obviously, it can be done only unpacking arguments right-to-left. I've tried to implement it following (wrong) way:
template<class T>
void operation(T first)
{
string(first);
}
template<class T, class U = float>
void operation(T first, U second)
{
number(first, second);
}
template<class ... ARGS>
void format(ARGS ... args)
{
auto last = (args, ...);
using last_type = typename decltype(last);
if constexpr (std::is_arithmetic_v<last_type>)
(..., operation(args, last));
else
(..., operation(args));
}
The problem is that while unpacking operation(args, last) we will get both args and last floats. I believe there's some easy way to achieve what I want (without relying on tuples etc).
Here is a proof-of-concept using only overload resolution. I'm not sure how scalable it is, though.
void format() {}
void format(char const *str) {
string(str);
}
template <class... Args>
void format(char const *str, char const *nextStr, Args... args);
template <class... Args>
void format(char const *str, double f, Args... args);
template <class... Args>
void format(char const *str, char const *nextStr, Args... args) {
string(str);
format(nextStr, args...);
}
template <class... Args>
void format(char const *str, double f, Args... args) {
number(str, f);
format(args...);
}
See it live on Godbolt.org
In a piece of code I'm writing, I receive packets as uint8_t * and std::size_t combination. I can register functions to call with these two parameters, based on which file descriptor the packet was received from. I use an std::map<int, std::function<void(const uint8_t *, std::size_t)> > handlers to keep track of which function to call.
I would like to be able to (indirectly) register functions with arbitrary arguments. I already have a function like this to transform from the uint8_t * and std::size_t to separate variables:
int unpack(const uint8_t *buf, std::size_t len) { return 0; }
template <typename T, typename... Types>
int unpack(const uint8_t *buf, std::size_t len, T &var1, Types... var2) {
static_assert(std::is_trivially_copyable<T>::value, "unpack() only works for primitive types");
if (len < sizeof(T)) return -1;
var1 = *reinterpret_cast<const T *>(buf);
const auto sum = unpack(buf + sizeof(T), len - sizeof(T), var2...);
const auto ret = (sum == -1) ? -1 : sum + sizeof(T);
return ret;
}
My question is: Is it possible with C++20 to auto-generate a function that convers from uint8_t * and std::size_t to the arguments that a passed function needs?
I would like to be able to do this:
void handler(unsigned int i) { ... }
int main(int argc, char ** argv) {
/* some code generating an fd */
handlers[fd] = function_returning_an_unpacker_function_that_calls_handler(handler);
edit: I realize I went a bit too short on my answer, as some mentioned (thanks!).
I am wondering if it is possible (and if so, how?) to implement the function_returning_an_unpacker_function_that_calls_handler function. I started out doing something like this (written from memory):
template<typename... Types>
std::function<void(const uint8_t * buf, std::size_t)>
function_returning_an_unpacker_function_that_calls_handler(std::function<void(Types...)> function_to_call) {
const auto ret = new auto([fun](const uint8_t * buf, std::size_t len) -> void {
const auto unpack_result = unpack(buf, len, list_of_variables_based_on_template_params);
if(unpack_result == -1) return nullptr;
function_to_call(list_of_variables_based_on_template_params);
};
return ret;
}
This is also why I supplied the unpack function. The problem I'm encountering is that I'm struggling with the list_of_variables_based_on_template_params bit. I haven't found any way to generate a list of variables that I can repeat identically in two places.
I also looked a little bit into using std::tuple::tie and friends, but I didn't see a solution there either.
It's possible, just annoying to write.
First you need a trait to get parameters from a function type:
template <typename T>
struct FuncTraits {};
#define GEN_FUNC_TRAITS_A(c, v, ref, noex) \
template <typename R, typename ...P> \
struct FuncTraits<R(P...) c v ref noex> \
{ \
template <template <typename...> typename T> \
using ApplyParams = T<P...>; \
};
#define GEN_FUNC_TRAITS_B(c, v, ref) \
GEN_FUNC_TRAITS_A(c, v, ref,) \
GEN_FUNC_TRAITS_A(c, v, ref, noexcept)
#define GEN_FUNC_TRAITS_C(c, v) \
GEN_FUNC_TRAITS_B(c, v,) \
GEN_FUNC_TRAITS_B(c, v, &) \
GEN_FUNC_TRAITS_B(c, v, &&)
#define GEN_FUNC_TRAITS(c) \
GEN_FUNC_TRAITS_C(c,) \
GEN_FUNC_TRAITS_C(c, volatile)
GEN_FUNC_TRAITS()
GEN_FUNC_TRAITS(const)
Then some templates to analyze what kind of callable (function, function pointer, or a functor) you got, and apply the trait accordingly:
template <typename T> struct RemoveMemPtr {using type = T;};
template <typename T, typename C> struct RemoveMemPtr<T C::*> {using type = T;};
template <typename T>
struct ToFuncType {};
template <typename T>
requires std::is_function_v<std::remove_pointer_t<T>>
struct ToFuncType<T> {using type = std::remove_pointer_t<T>;};
template <typename T>
requires requires {&T::operator();}
struct ToFuncType<T>
{
using type = typename RemoveMemPtr<decltype(&T::operator())>::type;
};
Then you can make a templated functor that automatically unwraps the arguments. Since Unwrap() must be called in order, and function arguments are evaluated in unspecified order, we need a tuple (or something similar) that accepts a braced list:
template <typename T>
T Unpack(char *&, std::size_t &)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return {};
}
template <typename F>
struct WrapFunctor
{
template <typename ...P>
struct Impl
{
std::decay_t<F> func;
void operator()(char *p, std::size_t n)
{
std::apply(func, std::tuple{Unpack<P>(p, n)...});
}
};
};
template <typename F>
auto Wrap(F &&func)
{
using Functor = typename FuncTraits<typename ToFuncType<std::remove_cvref_t<F>>::type>::template ApplyParams<WrapFunctor<F>::template Impl>;
return Functor{std::forward<F>(func)};
}
Finally, some tests:
void foo(int, float, char)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
int main()
{
Wrap(foo)(nullptr, 42);
Wrap(&foo)(nullptr, 42);
Wrap([](int, float, char){std::cout << __PRETTY_FUNCTION__ << '\n';})(nullptr, 42);
}
I've changed the signature of Unpack() to take the parameters by reference and unpack one variable at a time.
Almost forgot: var1 = *reinterpret_cast<const T *>(buf); is a strict aliasing violation and UB. Prefer memcpy.
This answer is very similar to the first one, but it leverages the use of CTAD and std::function to figure out the function signature.
Creates a tuple based on the function signature, and passes both the argument types and the elements from the tuple on to unpack.
#include <iostream>
#include <tuple>
#include <type_traits>
#include <cstring>
#include <functional>
int unpack(const uint8_t *buf, std::size_t len) { return 0; }
template <typename T, typename... Types>
int unpack(const uint8_t *buf, std::size_t len, T &var1, Types&... var2) {
static_assert(std::is_trivially_copyable<T>::value, "unpack() only works for primitive types");
if (len < sizeof(T)) return -1;
var1 = *reinterpret_cast<const T *>(buf);
std::cout << "In unpack " << var1 << "\n";
const auto sum = unpack(buf + sizeof(T), len - sizeof(T), var2...);
const auto ret = (sum == -1) ? -1 : sum + sizeof(T);
return ret;
}
template<typename T, typename R, typename... Args>
std::function<void(const uint8_t * buf, std::size_t)>
unpack_wrapper_impl(T function_to_call, std::function<R(Args...)>) {
return [function_to_call](const uint8_t *buf, std::size_t len) -> void {
std::tuple<std::decay_t<Args>...> tup;
std::apply([&](auto&... args) {
unpack(buf, len, args...);
}, tup);
std::apply(function_to_call, tup);
};
}
template<typename T>
std::function<void(const uint8_t * buf, std::size_t)>
unpack_wrapper(T&& function_to_call) {
return unpack_wrapper_impl(std::forward<T>(function_to_call), std::function{function_to_call});
}
void test(int a, int b) {
std::cout << a << " " << b << "\n";
}
int main() {
int a= 5, b = 9;
uint8_t* buf = new uint8_t[8];
std::memcpy(buf, &a, 4);
std::memcpy(buf + 4, &b, 4);
auto f = unpack_wrapper(test);
f(buf, 8);
}
I'm creating my own delegate implementation (I'm looking at C# delegates). I have an object-function binding struct
struct ObjFunc
{
void* obj = nullptr;
void* func = nullptr;
ObjFunc(void* obj, void* func)
: obj(obj)
, func(func)
{
}
};
And I have a Call function
void Call(InTypes... args)
{
auto bindings_copy = bindings_;
for (auto& binding : bindings_copy)
{
try
{
if (binding.obj)
{
(binding.obj->*static_cast<void(T::*)(InTypes...)>(binding.func))(std::forward<InTypes>(args)...); // <- Problem Here
}
else
{
(*static_cast<void(*)(InTypes...)>(binding.func))(std::forward<InTypes>(args)...);
}
}
catch (...)
{
//print_warning("Delegate", "Bad delegate binding on %s object %p and function %p", typeid(binding.obj).name(), binding.obj, binding.func);
}
}
}
Before call I have to cast my function ptr to member function of some class, for example T, but I don't know which class will be used be at runtime
Or, at least, tell how to identify function in some generic manner, so I'll be able to ensure that pointer to some unknown function differs from another pointer to some unknown function
Idea is to erase type from known type, something like:
struct ObjFunc
{
template <typename T, typename Ret, typename C, typename ... Args>
static call_mem(void* objPtr, void* funcPtr, Args... args)
{
T* obj = reinterpret_cast<T*>(objPtr);
Ret (*func)(Args...) = reinterpret_cast<Ret (C::*)(Args...)>(funcPtr);
(obj->*func)(args...);
}
template <typename T, typename Ret, typename C, typename ... Args>
static call_const_mem(void* objPtr, void* funcPtr, Args... args)
{
T* obj = reinterpret_cast<T*>(objPtr);
Ret (*func)(Args...) = reinterpret_cast<Ret (C::*)(Args...) const>(funcPtr);
(obj->*func)(args...);
}
template <typename Ret, typename ... Args>
static call_func(void* p, void* funcPtr, Args... args)
{
assert(p == nullptr);
Ret (*func)(Args...) = reinterpret_cast<Ret (*)(Args...)>(funcPtr);
func(args...);
}
void* obj = nullptr;
void* func = nullptr;
void* f = nullptr;
template <typename T, typename Ret, typename C, typename ... Args>
ObjFunc(T& obj, Ret (C::*func)(Args...) const)
: obj(&obj)
, func(func)
, f(call_const_mem<T, ret, C, Args...>)
{
}
template <typename T, typename Ret, typename C, typename ... Args>
ObjFunc(T& obj, Ret (C::*func)(Args...))
: obj(&obj)
, func(func)
, f(call_mem<T, ret, C, Args...>)
{
}
template <typename T, typename Ret, typename C, typename ... Args>
ObjFunc(Ret (*func)(Args...))
: obj(nullptr)
, func(func)
, f(call_func<T, ret, C, Args...>)
{
}
template <typename ...Ts>
void Call(Ts... args) const
{
reinterpret_cast<void(*)(void*, void*, Ts...)>(f)(obj, func, args...);
}
};
but simpler would be to use std::function.
Well, even if it's impossible to call a function from pointer, second task (identify function in uniform format) still may be solved. You can use memcpy to copy data from function pointer. Example:
template<typename FSIG>
char* get_function_identifier(const FSIG& func_pointer, int& size)
{
size = sizeof(FSIG);
char* result = new char[size];
memcpy(result, &func_pointer, size);
return result;
}
This function returns you char buffer, containing identifying info about function
P.S. Note that this char array is not a c-style string! it does not have terminating character, so strcmp and all other C string operations shouldn't be applied
I have a method that accepts format string + arguments (right as printf()), however, I'm using variadic templates for this purpose:
template<typename... Args>
static void log(const char* pszFmt, Args&&... args)
{
doSomething(pszFmt, std::forward<Args>(args)...);
}
Some of args can be std::string instances. Is it possible to make sure that doSomething will never accept std::string, but will always accept const char* instead of each source std::string passed to log()?
In other words, I need a way to forward all the args to doSomething() making all the std::string arguments substituted with what std::string::c_str() returns.
Thanks in advance!
You could define your own "forwarding" method:
template<typename T>
decltype(auto) myForward(T&& t)
{
return t;
}
template<>
decltype(auto) myForward(std::string& t)
{
return t.c_str();
}
template<>
decltype(auto) myForward(std::string&& t)
{
return t.c_str();
}
template<typename... Args>
static void log(const char* pszFmt, Args&&... args)
{
doSomething(pszFmt, myForward<Args>(std::forward<Args>(args))...);
}
C++17 version
You can use SFINAE to achieve this:
#include <iostream>
#include <utility>
#include <string>
template <typename, typename = void>
struct has_c_str : std::false_type {};
template <typename T>
struct has_c_str<T, std::void_t<decltype(&T::c_str)>> : std::is_same<char const*, decltype(std::declval<T>().c_str())>
{};
template <typename StringType,
typename std::enable_if<has_c_str<StringType>::value, StringType>::type* = nullptr>
static void log(const char* pszFmt, StringType const& arg) {
std::cout << "std::string version" << std::endl;
}
template <typename StringType,
typename std::enable_if<!has_c_str<StringType>::value, StringType>::type* = nullptr>
static void log(const char* pszFmt, StringType arg) {
std::cout << "const char * version" << std::endl;
}
template <typename... Args>
static void log(const char* pszFmt, Args&&... args) {
log(pszFmt, std::forward<Args>(args)...);
}
int main() {
log("str", std::string("aa")); // output: std::string version
log("str", "aa"); // output: const char * version
return 0;
}
Full demo here
Here's an alternative solution. If your logger simply prints each argument and doesn't "store" it, then there's no need to perfect-forward the arguments, a simple pass-by-reference will suffice.
In that case you can simply overload or specialize the printer function for various "printable" types.
template <class T>
decltype(auto) printer(T const& t) {
return t;
}
inline const char* printer(std::string const& t) {
return t.c_str();
}
template<typename... Args>
void log(const char* pszFmt, Args const&... args) {
printf(pszFmt, printer(args)...);
}
int main() {
std::string str{"xyz"};
log("%s %s %s\n", "abc", std::string("def"), str);
}
Note: the non-template overload will always be preferred during overload resolution.
Desired behavior
What I basically want is to create a function like this:
void func(std::string_view... args)
{
(std::cout << ... << args);
}
It should be able to work only with classes that are convertible to std::string_view.
Example:
int main()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
func(tmp1, tmp2, tmp3, "\n");
return 0;
}
should print: Hello World!
Accomplished behavior
So far, I got here:
template<typename... types>
using are_strings = std::conjunction<std::is_convertible<types, std::string_view>...>;
template<typename... strings, class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings... args)
{
(std::cout << ... << args);
}
int main()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
func(tmp1, tmp2, tmp3, "\n");
return 0;
}
This actually works as expected, but there is still one big problem.
Problem
Only classes that are convertible to std::string_view can be used in this function and that's great.
However, even though classes are convertible, they are not converted to std::string_view!
This leads to needless copying of data(for example when std::string is passed as argument).
Question
Is there a way to force implicit conversion of variadic arguments to std::string_view?
Note
I know about std::initializer_list, but I would like to keep function call simple, without {}.
namespace impl{
template<class...SVs>
void func(SVs... svs){
static_assert( (std::is_same< SVs, std::string_view >{} && ...) );
// your code here
}
}
template<class...Ts,
std::enable_if_t< (std::is_convertible<Ts, std::string_view >{}&&...), bool > =true
>
void func( Ts&&...ts ){
return impl::func( std::string_view{std::forward<Ts>(ts)}... );
}
or somesuch.
#include <string_view>
#include <utility>
template <typename>
using string_view_t = std::string_view;
template <typename... Ts>
void func_impl(string_view_t<Ts>... args)
{
(std::cout << ... << args);
}
template <typename... Ts>
auto func(Ts&&... ts)
-> decltype(func_impl<Ts...>(std::forward<Ts>(ts)...))
{
return func_impl<Ts...>(std::forward<Ts>(ts)...);
}
DEMO
If you simply want to avoid needless copying of data, use a forward reference and then perform explicit casts (if still required). This way no data is copied but forwarded (in your main.cpp example, all params are passed as const references)
template <typename... strings,
class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings&&... args) {
(std::cout << ... << std::string_view{args});
}
Not exactly what you asked... but if you can set a superior limit for a the length of args... (9 in following example) I propose the following solution: a foo<N> struct that inherit N func() static function that accepting 0, 1, 2, ..., N std::string_view.
This way, func() function are accepting what is convertible to std::string_view and all argument are converted to std::string_view.
That is exactly
void func(std::string_view... args)
{ (std::cout << ... << args); }
with the difference that func() functions are static methods inside foo<N>, that there is a limit in args... length and that there is a func() method for every supported length.
The full example is the following.
#include <string>
#include <utility>
#include <iostream>
#include <type_traits>
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename, typename>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
static void func (typename getType<T, Is>::type ... args)
{ (std::cout << ... << args); }
};
template <std::size_t N, typename = std::string_view,
typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, typename T, std::size_t ... Is>
struct foo<N, T, std::index_sequence<Is...>> : public bar<T, IndSeqFrom<Is>>...
{ using bar<T, IndSeqFrom<Is>>::func ...; };
int main ()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
foo<10u>::func(tmp1, tmp2, tmp3, "\n");
}
Make it a two-stage production:
template <class... Args>
std::enable_if_t<... && std::is_same<Args, std::string_view>()>
func(Args... args)
{
(std::cout << ... << args);
}
template <class... Args>
auto func(Args&&... args)
-> std::enable_if_t<... || !std::is_same<std::decay_t<Args>, std::string_view>(),
decltype(func(std::string_view(std::forward<Args>(args))...))>
{
func(std::string_view(std::forward<Args>(args))...);
}