Overloading between printf functions and fmt functions - c++

I have a class that takes a C printf set of variadic arguments in its constructor, like this:
class Foo {
public:
Foo(const char* str, ...) __attribute__((format(printf, 2, 3)));
Now I want to be able to use the fmt library with this class. If I were willing to change all callers I could switch it something like this:
class Foo {
public:
template<typename Str, typename... Args>
Foo(const Str& str, const Args&... args)
: Foo(str, fmt::make_args_checked<Args...>(str, args...))
{}
private:
Foo(fmt::string_view fmt, fmt::format_args args);
But this class is used in 100's of places and it's not feasible to "change the world". So I want to keep both constructors, but obviously now I need a way to choose between them. I'm not excited about having to add a new dummy parameter or something.
Then I thought, well, I'd also really like to enforce using the FMT_STRING() macro since my printf-style code takes advantage of printf format checking in GCC and clang. So maybe I could do something with that: I could create my own macro, say MYFMT(), that would invoke FMT_STRING() somehow, or at least do the same checking, but then resolve to my own type that could be used to choose a different constructor; something like:
#define MYFMT(_f) ...
class Foo {
public:
Foo(const char* str, ...);
Foo(const MyType& str, ...) ...
So, the usage would be something like:
auto x = Foo("this is a %s string", "printf");
auto y = Foo(MYFMT("this is a {} string"), "fmt");
But I have played with this for a few hours and tried to wrap my head around how the FMT_STRING macro works and what I'd need to do, and I can't come up with anything. Maybe this isn't possible for some reason but if anyone has any hints that would be great.
FWIW my base compilers are GCC 10, clang 9, and MSVC 2019 so I can rely on C++17 at least.

You can do it as follows (godbolt):
#include <fmt/core.h>
struct format_string {
fmt::string_view str;
constexpr operator fmt::string_view() const { return str; }
};
#define MYFMT(s) format_string{s}
class Foo {
public:
template <typename... T>
Foo(const char* str, T&&... args) {
fmt::print("printf\n");
}
template <typename... T>
Foo(fmt::format_string<T...> str, T&&... args) {
fmt::print("fmt\n");
}
};
int main() {
Foo("%d\n", 42); // calls the printf overload
Foo(MYFMT("{}\n"), 42); // calls the fmt overload
}
With C++20 this will give you compile-time checks in {fmt}. Note that varargs are replaced with variadic templates in the printf overload to avoid ambiguity so you won't be able to apply the format attribute. It might be possible to keep varargs by tweaking this solution a bit.
A better option would be to avoid overloading and macros altogether and use a different function instead:
class Foo {
private:
Foo() {}
public:
Foo(const char* str, ...) {
fmt::print("printf\n");
}
template <typename... T>
static Foo format(fmt::format_string<T...> str, T&&... args) {
fmt::print("fmt\n");
return Foo();
}
};

Related

Using {fmt} & source_location to create variadic-template-based logging function

I'd like to create a simple log function in C++ which prepend the code location to the log message. I'd like to avoid macros overall as well as the usage of __FILE__ & __LINE__.
Note that the format string is always a compile-time string, and I'd like to have a much computation as possible on compile time (the target machine is a small MCU).
I can use the C++20 source_location feature via experimental/source_location.
I can also use {fmt}.
I started off from this. Currently, I've got the following:
#include <fmt/format.h>
#include <experimental/source_location>
using source_location = std::experimental::source_location;
void vlog(fmt::string_view format, fmt::format_args args)
{
fmt::vprint(format, args);
}
template <typename S, typename... Args>
void log(const S& format, const source_location& location, Args&&... args)
{
vlog(format, fmt::make_args_checked<fmt::string_view, uint32_t, Args...>(format, location.file_name(), location.line(), args...));
}
#define MY_LOG(format, ...) log(FMT_STRING("{},{}, " format), source_location::current(), __VA_ARGS__)
int main() {
MY_LOG("invalid squishiness: {}", 42);
}
Which yields correctly ./example.cpp,20, invalid squishiness: 42.
Looks to me like I'm pretty close. I think what's left is to make the log function take a default argument for source_location (I understand that source_location::current() as a default arguement is a good practice). I'm getting the following error though:
:12:99: error: missing default argument on parameter 'args'
Is this even possible to mix variadic templates and default arguments for parameters? If so, how?
Also, is there a way to prepend the "{},{}, " part to the compile-time format string to yield yet another compile-time string (to be used as format)?
You can do it with a struct that represents the format string and location:
#include <fmt/core.h>
#include <source_location>
struct format_string {
fmt::string_view str;
std::source_location loc;
format_string(
const char* str,
const std::source_location& loc =
std::source_location::current()) : str(str), loc(loc) {}
};
void vlog(const format_string& format, fmt::format_args args) {
const auto& loc = format.loc;
fmt::print("{}:{}: ", loc.file_name(), loc.line());
fmt::vprint(format.str, args);
}
template <typename... Args>
void log(const format_string& format, Args&&... args) {
vlog(format, fmt::make_format_args(args...));
}
int main() {
log("invalid squishiness: {}", 42);
}
This prints:
./example.cpp:26: invalid squishiness: 42
Godbolt: https://godbolt.org/z/4aMKcW

Call function based on template argument type

There are two "C" functions:
void fooA(const char*);
void fooW(const wchar_t*);
Then there is a wrapper template function:
template<typename _TChar>
void foo(const _TChar* str)
{
// call fooA or fooB based on actual type of _TChar
// std::conditional .. ?
// fooA(str);
// fooW(str);
}
If the caller calls foo("Abc"), this template function should make a compile-time call to fooA. Similiarly, foo(L"Abc") should make the final call to fooW.
How do I do that? I thought of using std::conditional but couldn't make it.
I cannot make fooA or fooB overloaded, since these are C functions.
You can put all your wchar_t versions in a class template, say overloads and their char counter-parts in its specialization as shown below:
template<typename WideCharVersion>
struct overloads
{
void foo(wchar_t const * arg)
{
FooW(arg);
}
//more wchar_t functions
};
template<>
struct overloads<std::false_type>
{
void foo(char const * arg)
{
FooA(arg);
}
//more char functions
};
//a friendly alias!
template<typename T>
using is_wide_char = typename std::is_same<whar_t, T>::type;
And then you can use them as:
template<typename _TChar>
void foo(const _TChar* str)
{
overloads<is_wide_char<_TChar>>::foo(str);
}
Expression SFINAE makes it easy!
Other way is to use Expression SFINAE which does not require to you write anything like overloads and does the same job with less code:
template<typename _TChar>
void foo(const _TChar* str)
{
invokeOne(fooA, fooW, str);
}
And then you can implement invokeOne as:
template<typename F1, typename F2, typename ... Args>
auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f1(args...))
{
return f1(args...);
}
template<typename F1, typename F2, typename ... Args>
auto invokeOne(F1 f1, F2 f2, Args && ... args) -> decltype(f2(args...))
{
return f2(args...);
}
Have a look at the online demo.
In this approach, you don't have to add the overloads to the overloads class template and to its specialization. Instead you just pass them as arguments to invokeOne which calls the right overload for you.
Hope that helps.
Then overload another function. I'm assuming foo does more work and needs to be a template. Then call foo_forward_call, defined as such:
void foo_forward_call(char const* ptr) {
FooA(ptr);
}
void foo_forward_call(wchar_t const* ptr) {
FooW(ptr);
}
and at the call site:
template<typename _TChar>
void foo(const _TChar* str)
{
foo_forward_call(str);
}
in C++1z you'll be able to use constexpr if, but to be honest, I think that overloaded solution is still more readable.
template<typename _TChar>
void foo(const _TChar* str)
{
if constexpr(std::is_same<_TChar, char>::value) {
FooA(str);
} else {
FooW(str);
}
}
Alternatively, you can use Boost.Hana's overload:
template<typename _TChar>
void foo(const _TChar* str)
{
hana::overload(fooA, fooW)(str);
}
demo
By the way: you should eschew using underscore-capital-letter names in your programs. They're reserved for implementation for any use (i.e. macros) and might lead to nasty name clashes.
This seems like a really strange thing to do with templates. I would suggest using normal overloading instead:
void foo(const char* p) { fooA(p); }
void foo(const wchar_t* p) { fooW(p); }
If you insist on using template then you can do it like this:
template <typename T>
void foo(const T* p)
{
// Declare functions here so that calling fooW with const char*
// and 'calling' fooA with const wchar_t* would not cause compile error.
void fooA(const T*);
void fooW(const T*);
if (std::is_same<char, T>::value)
fooA(p);
else
fooW(p);
}
I like solving problems in general. So let us design a mechanism to overload stuff.
overload_t<...> takes a set of callables in ... and generates an object which uses standard overload resolution to choose between them, via inheritance of operator():
template<class...Fs>
struct overload_t;
// the case where we have a function object:
template<class F>
struct overload_t<F>:F{
overload_t(F f):F(std::move(f)){}
using F::operator();
// boilerplate to ensure these are enabled if possible:
overload_t(overload_t&&)=default;
overload_t(overload_t const&)=default;
overload_t& operator=(overload_t&&)=default;
overload_t& operator=(overload_t const&)=default;
};
// we cannot inherit from a function pointer. So
// store one, and write an `operator()` that forwards to it:
template<class R, class...Args>
struct overload_t<R(*)(Args...)>{
using F=R(*)(Args...);
F f;
overload_t(F fin):f(fin){}
R operator()(Args...args)const{
return f(std::forward<Args>(args)...);
}
overload_t(overload_t&&)=default;
overload_t(overload_t const&)=default;
overload_t& operator=(overload_t&&)=default;
overload_t& operator=(overload_t const&)=default;
};
// the case where we have more than type to overload.
// recursively inherit from the one-arg and the rest-of-arg
// and using operator() to bring both of their () into equal standing:
template<class F0, class...Fs>
struct overload_t<F0,Fs...>:
overload_t<F0>,
overload_t<Fs...>
{
using overload_t<F0>::operator();
using overload_t<Fs...>::operator();
overload_t(F0 f0, Fs...fs):
overload_t<F0>(std::move(f0)),
overload_t<Fs...>(std::move(fs)...)
{}
overload_t(overload_t&&)=default;
overload_t(overload_t const&)=default;
overload_t& operator=(overload_t&&)=default;
overload_t& operator=(overload_t const&)=default;
};
// a helper function to create an overload set without
// having to specify types. Will be obsolete in C++17:
template<class...Fs>
overload_t<Fs...> overload(Fs...fs){ return {std::move(fs)...};}
Now to generate a single object that is an overload of multiple, do this:
overload(FooA,FooW)( str );
And str will be dispatched to either based on the usual overload resolution rules. This is useful elsewhere which is why it is worth writing, and the code at point of use is self documenting.
Live example (wow, wrote it correct the first time!)
There are a number of improvements one can add to the above overload_t.
Perfect forwarding of the fs during construction and in the helper function.
Balanced Binary tree inheritance instead of linear (important of more than a few overloads are done). This can have runtime and compiletime performance impliciations, especially for a large number of functions.
Introspect incoming Fs; if they are overload_t balance the combined tree.
In C++17, a func<auto> template that takes a function pointer and returns a function object that statelessly invokes it. Compilers are relatively good at eliding function pointers, but they are better at it when there is no possible runtime state that could alter them.
Deciding what to do for overload_t<>. Currently it fails to compile; maybe it should just be an empty struct {}, or even a struct with an uncallable operator().
Examine existing libraries, like boost::hana::overload and see what differences there are.
Expose the ability to extract which of the overloads would be called, maybe via a static tag_t<F> which_overload_helper( Args... ) const method and template<class...Args> using which_overload = typename decltype( which_overload_helper( std::declval<Args>()... ) )::type;
Properly picking overloads when some of the incoming Fss do/do not have a const operator(). Should the function pointer have const, volatile, both or neither on operator()? All 4? How about && vs & overloads?
Maybe if there were more targetted functions or overloading wasn't the only problem you were trying to solve then it might be worthwhile using templates to get this done.
In this case though, simply write your overloads:
void foo(const char* x) { fooA(x); }
void foo(const wchar_t* x) { fooW(x); }
You have a few options.
Use an helper explicitly specialized struct:
template <typename>
struct helper;
template<>
struct helper<char>
{
void operator()(const char* x){ FooA(x); }
};
template<>
struct helper<wchar_t>
{
void operator()(const wchar_t* x){ FooW(x); }
};
template <typename _TChar>
void foo(const _TChar* str)
{
helper<_TChar>{}(str);
}
Use a "static if" implementation (e.g. boost::hana::eval_if or my own):
template <typename _TChar>
void foo(const _TChar* str)
{
vrm::core::static_if(std::is_same<_TChar, char>{})
.then([](const auto* x_str){ FooA(x_str); })
.else_([](const auto* x_str){ FooW(x_str); })(str);
}
Use an helper overloaded function:
void helper(const char* x) { FooA(x); }
void helper(const wchar_t* x) { FooW(x); }
template <typename _TChar>
void foo(const _TChar* str)
{
helper(str);
}
I think using typeid() is very elegant and readable, but not sure if there is any side effect.
#include <typeinfo>
template <typename T>
void foo(){
if(typeid(T) == typeid(const char*)){
fooA();
}
else if (typeid(T) == typeid(const wchar_t*)){
fooB();
}
}
Check it out in wandbox.

How do I template a function that takes templated args and applies a templated function on them in c++?

I have a bunch of static class functions that take in varying number of {string, int, float} params and an Output param. There may be different behavior for the same parameters based on the function called. For example:
static void ChangeOutput1(const string& foo, int bar, Output* output);
static void ChangeOutput2(int bar, Output* output);
static void ChangeOutput3(float foo, Output* output);
static void ChangeOutput4(float foo, Output* output); // behaves differently from ChangeOutput3
I'd like to have a simple, safe way of writing templates to perform a similar behavior over each of the functions, basically calling with an Output argument and converting it to a string to return. Ideally without having to specify the parameter types again. It might look something like this:
template<typename... Args, int (*ChangeOutputFn)(Args...)>
string OutputString(Args... args) {
Output output;
ChangeOutputFn(args..., &output);
return ConvertToString(output);
}
// Is there a way to alias static templated functions?
using StringOutput1 = OutputString<ChangeOutput1>;
using StringOutput2 = OutputString<ChangeOutput2>;
using StringOutput3 = OutputString<ChangeOutput3>;
I'm not sure how to achieve this. I'm both unsure of how to write OutputString and how I would alias or define the static functions. There are less elegant solutions, but they require repetitive boilerplate that I'd like to avoid.
With a class, you may do something like:
template <typename T, T f> struct OutputString;
template<typename... Args, void (*ChangeOutputFn)(Args...)>
struct OutputString<void (*)(Args...), ChangeOutputFn>
{
template <typename ... Ts>
auto operator()(Ts... args)
-> decltype(ChangeOutputFn(std::forward<Ts>(args)..., std::declval<Output *>()),
std::string{})
{
Output output;
ChangeOutputFn(std::forward<Ts>(args)..., &output);
return ConvertToString(output);
}
};
And then
using StringOutput1 = OutputString<decltype(&ChangeOutput1), &ChangeOutput1>;
using StringOutput2 = OutputString<decltype(&ChangeOutput2), &ChangeOutput2>;
using StringOutput3 = OutputString<decltype(&ChangeOutput3), &ChangeOutput3>;
and use it as
std::string s2 = StringOutput2{}(42);
std::string s3 = StringOutput3{}(4.2f);
Demo
You can do this if you move the Output argument to the front.
static void ChangeOutput1(Output*, const std::string& foo, int bar);
static void ChangeOutput2(Output*, int bar);
static void ChangeOutput3(Output*, float foo);
static void ChangeOutput4(Output*, float foo);
Now you can have this template:
template<typename... Args>
std::function<std::string(Args...)>
mkOutput (void (*ChangeOutputFn)(Output*, Args...))
{
return [ChangeOutputFn](Args... args)->std::string{
Output output;
ChangeOutputFn(&output, args...);
return ConvertToString(output);
};
}
and "function aliases" look like this:
auto a1 = mkOutput(ChangeOutput1);
auto a2 = mkOutput(ChangeOutput2);
auto a3 = mkOutput(ChangeOutput3);
auto a4 = mkOutput(ChangeOutput4);
Note 1. You cannot have this syntax
OutputString<ChangeOutput1>
because ChangeOutput1 is a value and OutputString must either know its type beforehand or receive it as another template argument.
It is possible to have something like this
OutputString<decltype(ChangeOutput1), ChangeOutput1>
and then eliminate the repetition with a macro, but that's just ugly.
I choose to pass the function at run time rather than at compile time, it's easier this way.

Pass overloaded member function to a variadic template function

I have a class with a function add:
class Pool {
public:
Pool() {};
template<class F, class... A>
auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F(A...)>::type>
{
// return empty placeholder, for the sake of this code example
std::future<typename std::result_of<F(A...)>::type> ret;
return ret;
};
};
It should take any function with its arguments, add it to a thread pool and return a future of the result type of that function.
And a class where I use it:
class MyClass {
public:
string doIt(string) { return string("a"); };
string doIt(int, string) { return string("b"); };
void test() {
Pool myPool;
string a("test");
myPool.add(&MyClass::doIt, a); // Error
};
};
Which gives a compiler error:
Error 1 error C2914: 'Pool::add' : cannot deduce template argument as function argument is ambiguous MyClass.cpp 94
Now the problem is (I think) that the compiler can't deduce which overload I want to use. Similar to Overloaded function as argument of variadic template function.
(Also I'm not 100% clear on why I have to use "&" for class member functions, but no ampersand if I pass in a free function).
Anyway I also tried the workaround mentioned in above answer:
struct doIt_wrapper {
template <typename... T>
auto operator()(T... args) -> decltype(doIt(args...)) {
return doIt(args...);
}
};
and then modifying MyClass::test() to:
void test() {
Pool myPool;
string a("test");
myPool.add(doIt_wrapper(), a);
};
But it also gives me a compiler error:
error C2893: Failed to specialize function template 'unknown-type doIt_wrapper::operator ()(T...)' C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xrefwrap 58
I also tried a few variants like myPool.add(doIt_wrapper<string>() and with/without '&' but they all generate one or the other compiler error.
I think I don't fully understand the problem yet and I would be glad if someone could shed light on it. Also I am looking for a proper solution to this problem. It can't really be the case that this only works as long as there are no two functions with the same name, and as soon as there are, everything breaks down without a proper, generic solution?
Edit: Fixed a few typos and uploaded a minimal example here: http://ideone.com/eX1r1l
As others have mentioned, the problem is that doIt() is not callable inside the doIt_wrapper class as it also needs a pointer to the object called on.
You could just modify the doIt_wrapper operator() to also take a pointer to the object and pass a pointer to this as first argument to add().
It would then look something like this:
#include <iostream>
#include <future>
using namespace std;
class Pool {
public:
Pool() {};
template<class F, class... A>
auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F&&(A&&...)>::type>
{
// return empty placeholder, for the sake of this code example
std::future<typename std::result_of<F&&(A&&...)>::type> ret;
return ret;
};
};
class MyClass {
public:
string doIt(string) { return string("a"); };
string doIt(int, string) { return string("b"); };
struct doIt_wrapper
{
template<class T, class... Ts>
auto operator()(T&& t, Ts&&... args) -> decltype(t->doIt(std::forward<Ts>(args)...))
{
return t->doIt(std::forward<Ts>(args)...);
}
};
void test() {
Pool myPool;
string a("test");
myPool.add(doIt_wrapper(), this, a); // No error no more
};
};
int main() {
// your code goes here
MyClass my;
my.test();
return 0;
}
This way you don't have to do the casts. The code compiles on both GCC and Clang.
You may use lambda:
myPool.add([this](const std::string& s) {doIt(s);}, a);
or even
myPool.add([this, a]() {doIt(a);});
Currently, you may indicate which overload to use that way:
myPool.add(static_cast<std::string (MyClass::*) (std::string)>(&MyClass::doIt), a);
Note that doIt is a method (not a free function or static function), so you have to call it with an object.
If you add static to doIt, you may choose the overload with
myPool.add(static_cast<std::string (*) (std::string)>(&MyClass::doIt), a);
The problem is that non-static member functions have a hidden, implicit this parameter that points to the instance that called it. Your compiler is right to reject it as it doesn't have the correct arguments for the function. Sending in this as an additional bound parameter will work:
myPool.add(&MyClass::doIt, this, a);
// ^^^^
Using a lamda expression will work as well.
Also, the standard library function std::async() already does what you're trying to do here, and more. Consider using that instead.
Edit: You also need to cast to the correct type to select the correct overload. #Jarod42 already shows you how.

Is &decltype(object)::memfn a misuse?

I had some class like this:
class Test {
public:
bool bar(int &i, char c) // some arguments are passed by ref, some are by value
{/*...*/}
bool foo(/*...*/)
{}
};
And I don't want repeatly call bar1/bar2, etc. and then check the return value again and again, so I wrote a macro and variadic template to handle those things
#define help_macro(object, memfn, ...) help_func(#object "." #memfn, \
object, &decltype(object)::memfn, ##__VA_ARGS__)
template<class T, typename Func, typename... Args>
void help_func(char const * name, T &&object, Func memfn, Args&&... args)
{
auto ret = (object.*memfn)(forward<Args>(args)...);
cout<<name<<":\t"
<<(ret ? "OK" : "Oops") // maybe I'll throw an exception here
<<endl;
}
And use it like this
int i = 0;
Test t;
help_macro(t, bar, i, 'a');
It works on g++-4.7/Debian, but ICC13.0/Win refuses to compile it(a very strange error message)
main.cpp(37): error : type name is not allowed
help_macro(t, bar, i, 'a');
^
main.cpp(37): error : expected a ")"
help_macro(t, bar, i, 'a');
^
I turn on the C++11 for ICC, and confirmed that ICC13 support variadic template and decltype
Do I use it incorrectly or it's ICC's problem?
Edit: Having actually bothered to test my theory it turns out I was wrong, in that context decltype(t) is Test as can be shown by a static_assert(std::is_same<decltype(t), Test>::value, "not a reference")
So ICC (or the EDG front end it uses) probably just doesn't properly support using decltype in nested-name-specifiers, which was changed by DR 743
Using std::decay does make ICC accept it though, and so is a useful workaround.
Original, wrong, answer:
I think ICC is right here, decltype(object) is actually Test& and a reference type cannot have members, so &decltype(t)::memfn is ill-formed.
The code can be simplified to:
struct Test {
void foo() {}
};
int main()
{
Test t;
auto p = &decltype(t)::foo;
}
Which G++ and Clang accept, but ICC rejects, correctly IMHO.
You can fix it by using std::remove_reference or std::decay
#include <type_traits>
// ...
Test t;
auto p = &std::decay<decltype(t)>::type::foo;
I think the reason is there is no 'bar' function in class Test but not sure as I don't have access to this compiler. However the error message you posted shows there is an attempt to use 'bar'.
The following works on both gcc and clang
class Test {
public:
bool bar1(int &i, char c) // some arguments are passed by ref, some are by value
{return true;}
bool bar2(int &i, char c)
{return true;}
};
#define help_macro(object, memfn, ...) help_func(#object "." #memfn, \
object, &decltype(object)::memfn, ##__VA_ARGS__)
template<class T, typename Func, typename... Args>
void help_func(char const * name, T &&object, Func memfn, Args&&... args)
{
auto ret = (object.*memfn)(std::forward<Args>(args)...);
std::cout<<name<<":\t"
<<(ret ? "OK" : "Oops") // maybe I'll throw an exception here
<<std::endl;
}
int main()
{
int i = 0;
Test t;
//help_macro(t, bar, i, 'a');
help_macro(t, bar2, i, 'a');
}