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
Related
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))...);
}
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!
I use a formatting library called fmt (http://fmtlib.net/latest/).
One of the possible use is :
fmt::format("Hello, {name}! The answer is {number}. Goodbye, {name}.", fmt::arg("name", "World"), fmt::arg("number", 42));
I'd like to wrap this call in a function that I'd call as :
myFunction(myString, {"name", "World"}, {"number", 42});
for any number of arguments.
Up to now,I only succeeded to do a function callable with a list of pairs :
myFunction(myString, std::make_pair("name", "World"), std::make_pair("number", 42));
with the function :
std::string myFunction(const char* str, const Args&... rest) const
{
return fmt::format(mLocale.getPOILabel(label), fmt::arg(rest.first, rest.second)...);
}
but I'd like not to have use pairs.
What should I do ?
PS : The fmt::arg cannot be passed between functions.
Not exactly what do you asked, because you have to call your function as
myFunction(myString, "name", "World", "number", 42);
instead of
myFunction(myString, {"name", "World"}, {"number", 42});
but the following should be an example (sorry but baz() is untested)
#include <tuple>
#include <string>
#include <utility>
#include <iostream>
// fmt headers
template <typename ... Ps>
std::string baz (char const * frmt,
std::pair<char const *, Ps> const & ... ps)
{ return fmt::format(frmt, fmt::arg(ps.first, ps.second)...); }
template <typename ... Ts, std::size_t ... Is>
std::string bar (char const * frmt, std::tuple<Ts...> const & t,
std::index_sequence<Is...> const &)
{ return baz(frmt, std::get<Is>(t)...); }
template <typename ... Ts>
std::string foo (char const * frmt, std::tuple<Ts...> const & t)
{ return bar(frmt, t, std::make_index_sequence<sizeof...(Ts)>{}); }
template <typename ... Ts, typename P1, typename P2, typename ... Args>
std::string foo (char const * frmt, std::tuple<Ts...> const & t,
P1 const & p1, P2 const & p2, Args const & ... args)
{ return foo(frmt,
std::tuple_cat(t, std::make_tuple(std::make_pair(p1, p2))), args...); }
template <typename ... Args>
std::string myFunction (char const * frmt, Args const & ... args)
{ return foo(frmt, std::tuple<>{}, args...); }
int main()
{
myFunction("format", "name", "World", "number", 42);
}
Observe that this example use std::index_sequence and std::make_index_sequence(), so compile starting from C++14; but it's easy to create a substitute for this class and this function to work with C++11 too.
Try moving the results of fmt::arg, rather than copying or passing const references.
template<typename ... Args>
std::string myFunction(const char* str, Args&&... rest) const
{
return fmt::format(mLocale.getPOILabel(label), std::forward<Args...>(rest)...);
}
myFunction(myString, fmt::arg("name", "World"), fmt::arg("number", 42));
In my current setup, I have a
typedef std::function<void (MyClass&, std::vector<std::string>) MyFunction;
std::map<std::string, MyFunction> dispatch_map;
And I register my functions in it with a macro. However, I have a problem with this: the parameters are passed as a vector of strings, which I have to convert inside the functions. I would rather do this conversion outside the functions, at the dispatcher level. Is this possible? The function signatures are known at compile time, and never change at run time.
You can get pretty far with variadic templates and some template/virtual techniques. With the following codes, you'll be able to do something like:
std::string select_string (bool cond, std::string a, std::string b) {
return cond ? a : b;
}
int main () {
Registry reg;
reg.set ("select_it", select_string);
reg.invoke ("select_it", "1 John Wayne"));
reg.invoke ("select_it", "0 John Wayne"));
}
output:
John
Wayne
Full implementation:
These codes are exemplary. You should optimize it to provide perfect forwarding less redundancy in parameter list expansion.
Headers and a test-function
#include <functional>
#include <string>
#include <sstream>
#include <istream>
#include <iostream>
#include <tuple>
std::string select_string (bool cond, std::string a, std::string b) {
return cond ? a : b;
}
This helps us parsing a string and putting results into a tuple:
//----------------------------------------------------------------------------------
template <typename Tuple, int Curr, int Max> struct init_args_helper;
template <typename Tuple, int Max>
struct init_args_helper<Tuple, Max, Max> {
void operator() (Tuple &, std::istream &) {}
};
template <typename Tuple, int Curr, int Max>
struct init_args_helper {
void operator() (Tuple &tup, std::istream &is) {
is >> std::get<Curr>(tup);
return init_args_helper<Tuple, Curr+1, Max>() (tup, is);
}
};
template <int Max, typename Tuple>
void init_args (Tuple &tup, std::istream &ss)
{
init_args_helper<Tuple, 0, Max>() (tup, ss);
}
This unfolds a function pointer and a tuple into a function call (by function-pointer):
//----------------------------------------------------------------------------------
template <int ParamIndex, int Max, typename Ret, typename ...Args>
struct unfold_helper;
template <int Max, typename Ret, typename ...Args>
struct unfold_helper<Max, Max, Ret, Args...> {
template <typename Tuple, typename ...Params>
Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params)
{
return fun (params...);
}
};
template <int ParamIndex, int Max, typename Ret, typename ...Args>
struct unfold_helper {
template <typename Tuple, typename ...Params>
Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params)
{
return unfold_helper<ParamIndex+1, Max, Ret, Args...> ().
unfold(fun, tup, params..., std::get<ParamIndex>(tup));
}
};
template <typename Ret, typename ...Args>
Ret unfold (Ret (*fun) (Args...), std::tuple<Args...> tup) {
return unfold_helper<0, sizeof...(Args), Ret, Args...> ().unfold(fun, tup);
}
This function puts it together:
//----------------------------------------------------------------------------------
template <typename Ret, typename ...Args>
Ret foo (Ret (*fun) (Args...), std::string mayhem) {
// Use a stringstream for trivial parsing.
std::istringstream ss;
ss.str (mayhem);
// Use a tuple to store our parameters somewhere.
// We could later get some more performance by combining the parsing
// and the calling.
std::tuple<Args...> params;
init_args<sizeof...(Args)> (params, ss);
// This demondstrates expanding the tuple to full parameter lists.
return unfold<Ret> (fun, params);
}
Here's our test:
int main () {
std::cout << foo (select_string, "0 John Wayne") << '\n';
std::cout << foo (select_string, "1 John Wayne") << '\n';
}
Warning: Code needs more verification upon parsing and should use std::function<> instead of naked function pointer
Based on above code, it is simple to write a function-registry:
class FunMeta {
public:
virtual ~FunMeta () {}
virtual boost::any call (std::string args) const = 0;
};
template <typename Ret, typename ...Args>
class ConcreteFunMeta : public FunMeta {
public:
ConcreteFunMeta (Ret (*fun) (Args...)) : fun(fun) {}
boost::any call (std::string args) const {
// Use a stringstream for trivial parsing.
std::istringstream ss;
ss.str (args);
// Use a tuple to store our parameters somewhere.
// We could later get some more performance by combining the parsing
// and the calling.
std::tuple<Args...> params;
init_args<sizeof...(Args)> (params, ss);
// This demondstrates expanding the tuple to full parameter lists.
return unfold<Ret> (fun, params);
}
private:
Ret (*fun) (Args...);
};
class Registry {
public:
template <typename Ret, typename ...Args>
void set (std::string name, Ret (*fun) (Args...)) {
funs[name].reset (new ConcreteFunMeta<Ret, Args...> (fun));
}
boost::any invoke (std::string name, std::string args) const {
const auto it = funs.find (name);
if (it == funs.end())
throw std::runtime_error ("meh");
return it->second->call (args);
}
private:
// You could use a multimap to support function overloading.
std::map<std::string, std::shared_ptr<FunMeta>> funs;
};
One could even think of supporting function overloading with this, using a multimap and dispatching decisions based on what content is on the passed arguments.
Here's how to use it:
int main () {
Registry reg;
reg.set ("select_it", select_string);
std::cout << boost::any_cast<std::string> (reg.invoke ("select_it", "0 John Wayne")) << '\n'
<< boost::any_cast<std::string> (reg.invoke ("select_it", "1 John Wayne")) << '\n';
}
If you can use boost, then here's an example of what I think you're trying to do ( although might work with std as well, I stick with boost personally ):
typedef boost::function<void ( MyClass&, const std::vector<std::string>& ) MyFunction;
std::map<std::string, MyFunction> dispatch_map;
namespace phx = boost::phoenix;
namespace an = boost::phoenix::arg_names;
dispatch_map.insert( std::make_pair( "someKey", phx::bind( &MyClass::CallBack, an::_1, phx::bind( &boost::lexical_cast< int, std::string >, phx::at( an::_2, 0 ) ) ) ) );
dispatch_map["someKey"]( someClass, std::vector< std::string >() );
However, as this sort of nesting quickly becomes fairly unreadable, it's usually best to either create a helper ( free function, or better yet a lazy function ) that does the conversion.
If I understand you correctly, you want to register void MyClass::Foo(int) and void MyClass::Bar(float), accepting that there will be a cast from std::string to int or float as appropriate.
To do this, you need a helper class:
class Argument {
std::string s;
Argument(std::string const& s) : s(s) { }
template<typename T> operator T { return boost::lexical_cast<T>(s); }
};
This makes it possible to wrap both void MyClass::Foo(int) and void MyClass::Bar(float) in a std::function<void(MyClass, Argument))>.
Interesting problme. This is indeen not trivial in C++, I wrote a self-contained implementation in C++11. It is possible to do the same in C++03 but the code would be (even) less readable.
#include <iostream>
#include <sstream>
#include <string>
#include <functional>
#include <vector>
#include <cassert>
#include <map>
using namespace std;
// string to target type conversion. Can replace with boost::lexical_cast.
template<class T> T fromString(const string& str)
{ stringstream s(str); T r; s >> r; return r; }
// recursive construction of function call with converted arguments
template<class... Types> struct Rec;
template<> struct Rec<> { // no parameters
template<class F> static void call
(const F& f, const vector<string>&, int) { f(); }
};
template<class Type> struct Rec< Type > { // one parameter
template<class F> static void call
(const F& f, const vector<string>& arg, int index) {
f(fromString<Type>(arg[index]));
}
};
template<class FirstType, class... NextTypes>
struct Rec< FirstType, NextTypes... > { // many parameters
template<class F> static void call
(const F& f, const vector<string>& arg, int index) {
Rec<NextTypes...>::call(
bind1st(f, fromString<FirstType>(arg[index])), // convert 1st param
arg,
index + 1
);
}
};
template<class... Types> void call // std::function call with strings
(const function<void(Types...)>& f, const vector<string>& args) {
assert(args.size() == sizeof...(Types));
Rec<Types...>::call(f, args, 0);
}
template<class... Types> void call // c function call with strings
(void (*f)(Types...), const vector<string>& args) {
call(function<void(Types...)>(f), args);
}
// transformas arbitrary function to take strings parameters
template<class F> function<void(const vector<string>&)> wrap(const F& f) {
return [&] (const vector<string>& args) -> void { call(f, args); };
}
// the dynamic dispatch table and registration routines
map<string, function<void(const vector<string>&)> > table;
template<class F> void registerFunc(const string& name, const F& f) {
table.insert(make_pair(name, wrap(f)));
}
#define smartRegister(F) registerFunc(#F, F)
// some dummy functions
void f(int x, float y) { cout << "f: " << x << ", " << y << endl; }
void g(float x) { cout << "g: " << x << endl; }
// demo to show it all works;)
int main() {
smartRegister(f);
smartRegister(g);
table["f"]({"1", "2.0"});
return 0;
}
Also, for performances, it's better to use unordered_map instead of map, and maybe avoid std::function overhead if you only have regular C functions. Of course this is only meaningful if dispatch time is significant compared to functions run-times.
No, C++ provides no facility for this to occur.