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;
}
Related
Generally, I would like to implement a SerialLog class containing a collection of methods that format strings using a printf-style API and output the compiled message to the Arduino serial console using the Serial.print() method contained within the basic arduino library.
Such code would enable much cleaner serial console log invocations within my code (the below begins by showing the required nested function calls of the core Arduino c++ library, whereas the latter two calls show the API I would like to implement):
# normal debug logging for Arduino using the provided functions
Serial.log(sprintf("A log message format string: %d/%f", 123, 456.78));
# the call I would like to use for string formatting
String message = SerialLog.sprintf("A log message format string: %d/%f", 123, 456.78);
# the call I would like to use to output a formatted string
SerialLog.printf("A log message format string: %d/%f", 123, 456.78);
As can be seen in the above examples, my intention is to create a set of class methods for serial console output with arguments that mirror the normal C++ printf function.
I've attempted to implement such a printf-style API using simple variadic definitions like myVariadicPrintfFunction(const char * format, ...) but such a function definition appears to require that all arguments are of type const char *. This is not the behavior I want. As such, my current implementation uses templates to enable arguments of any type (obviously the type must be ultimately acceptable to the C++ core printf function, though).
My implementation includes the following public methods within a SerialLog class:
SerialLog::sprint (String sprint(const char * format)): Accepts a const char * argument. Returns the string as the Arduino String object.
SerialLog::sprintf (template <typename ...Args> String sprintf(const char * format, Args ...args)): Accepts a const char * argument as the format string and any number of additional arguments (of various types) which will be substituted within the format string. Returns the string as the Arduino String object.
SerialLog::print (SerialLog& print(const char * format)): Same as SerialLog::sprint to output the string to the serial console using Serial.print() instead of simply returning it.
SerialLog::printf (template <typename ...Args> SerialLog& printf(const char * format, Args ...args)): Uses the return value of SerialLog::sprintf to output the string to the serial console using Serial.print() instead of simply returning it.
As with the normal C++ printf function, both SerialLog::sprintf and SerialLog::printf must accept a format string as the first argument followed by any number of acceptable argument of any acceptable type which are used as the substitution values for the provided format string.
For example, a format of "This %s contains %d substituted %s such as this float: %d." with additional arguments of string (as a char *), 4 (as an int), "values" (as a char *), and 123.45 (as a float) would result in the following compiled string: "This string contains 4 substituted values such as this float: 123.45.".
I have been unable to achieve the described behavior using the following code:
debug.h
#include <stdio.h>
#include <Arduino.h>
namespace Debug
{
class SerialLog
{
public:
String sprint(const char * format);
template <typename ...Args>
String sprintf(const char * format, Args ...args);
SerialLog& print(const char * format);
template <typename ...Args>
SerialLog& printf(const char * format, Args ...args);
} /* END class SerialLog */
} /* END namespace Debug */
debug.cpp
#include <debug.h>
namespace Debug
{
String SerialLog::sprint(const char * format)
{
return String(format);
}
template <typename ...Args>
String SerialLog::sprintf(const char * format, Args ...args)
{
char buffer[256];
snprintf(buffer, 256, format, args...);
return String(buffer);
}
SerialLog& SerialLog::print(const char * format)
{
Serial.print(format);
return *this;
}
template <typename ...Args>
SerialLog& SerialLog::printf(const char * format, Args ...args)
{
Serial.print(this->sprintf(format, args...));
return *this;
}
} /* END namespace Debug */
At this time, the follow errors occur during compilation:
C:\Temp\ccz35B6U.ltrans0.ltrans.o: In function `setup':
c:\arduino-app/src/main.cpp:18: undefined reference to `String RT::Debug::SerialLog::sprintf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:22: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:26: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:29: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:30: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*, int, double>(char const*, char const*, int, double)'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\debug\firmware.elf] Error 1
Note: The above code is extracted from a larger Debug namespace and an expanded SerialLog class that contains additional methods, so the following error message line numbers will not correctly represent the example code shown.
The full VSCode build log (using the PlatformIO extension) can be located as a Gist at gist.github.com/robfrawley/7ccbdeffa064ee522a18512b77d7f6f9. Moreover, the entire project codebase can be referenced at github.com/src-run/raspetub-arduino-app, with the relevant projects for this question located at lib/Debug/Debug.h and lib/Debug/Debug.cpp.
Lastly, while I am proficient in many other languages like Python, PHP, Ruby, and others, this is the first C++ project! I am learning the C++ language through this application's implementation and am aware that many suboptimal choices exist within the codebase; different aspects of this application will be amended and improved as my knowledge of C++ evolves. As such, I am not particularly interested in comments regarding deficiencies in my implementation or verbose opinion pieces explaining the shortcomings in my understanding of C++. Please keep any discussion focused on the singular question outlined above.
Thanks for taking the time to read through this entire question and I greatly appreciate any assistance provided!
Not sure (without a full example it's difficult) but I suppose the problem is that you've declared only the template methods inside debug.h and you have defined them inside debug.cpp.
A general suggestion: in C++, ever declare and define template things (classes, functions, methods, variables) inside header files
The point is that, in this case, the compiler implement the specific template method when is needed. So if you write in main.cpp
char const * bar = "bar";
RT::Debug::SerialLog::printf("foo format: %s %i %lf", bar, 0, 1.1);
the compiler know that needs a RT::Debug::SerialLog::printf<char const*, int, double> but can't implement it because, in main.cpp, see only the content of debug.h, where the template method SerialLog::printf() is declared but not defined. So the compiler can't implement the char const *, int, double version of the method.
I suggest to change the files as follows
--- debug.h
#include <stdio.h>
#include <Arduino.h>
namespace Debug
{
class SerialLog
{
public:
String sprint(const char * format);
template <typename ...Args>
String sprintf(const char * format, Args ...args)
{
char buffer[256];
snprintf(buffer, 256, format, args...);
return String(buffer);
}
SerialLog& print(const char * format);
template <typename ...Args>
SerialLog& printf(const char * format, Args ...args)
{
Serial.print(this->sprintf(format, args...));
return *this;
}
} /* END class SerialLog */
} /* END namespace Debug */
--- debug.cpp
#include <debug.h>
namespace Debug
{
String SerialLog::sprint(const char * format)
{
return String(format);
}
SerialLog& SerialLog::print(const char * format)
{
Serial.print(format);
return *this;
}
} /* END namespace Debug */
---- end files
This way, if you write in main.cpp
RT::Debug::SerialLog::printf("foo format: %s %i %lf", bar, 0, 1.1);
the compiler know that needs a RT::Debug::SerialLog::printf<char const*, int, double> and can implement it because can see, from debug.h, the definition of SerialLog::printf().
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.
In C, I can do this to have an unspecified number of arguments in a function:
#include <elf.h>
#include <stddef.h>
#include <stdlib.h>
extern char **__environ;
int __libc_start_main
(
int (*main)(),
int argc,
char **argv
)
{
int ret;
Elf32_auxv_t *auxv;
size_t aux[38];
/* ... */
exit(main(argc, argv, __environ, aux));
}
However, when doing this in C++, the compiler emits this error:
test.c: In function ‘int __libc_start_main(int (*)(), int, char**)’:
test.c:21:45: error: too many arguments to function
exit(main(argc, argv, __environ, aux));
^
How do I do this in C++?
I understand that the C/C++ standards don't allow this, but I'm currently writing an implementation of the standard C library.
The short answer is: You don't.
In C++ all functions have a prototype; there is no such thing as an "unspecified number of arguments".
If you want to call main as main(argc, argv, __environ, aux), you need to declare it as int (*main)(int, char **, char **, void *) or similar.
Try either:
void foo(...);
or
template <typename ... ARGS> void foo(ARGS && ... args) { ... body }
First option is the same as void foo() (little known C language fact). First option requires some sort of additional argument (for example printf(char *, ...), where first argument allows function to detect, how to parse following arguments).
Second option requires you to commit to a function body somewhere in a header.
In c++03 and earlier to disable compiler warning about unused parameter I usually use such code:
#define UNUSED(expr) do { (void)(expr); } while (0)
For example
int main(int argc, char *argv[])
{
UNUSED(argc);
UNUSED(argv);
return 0;
}
But macros are not best practice for c++, so.
Does any better solution appear with c++11 standard? I mean can I get rid of macros?
Thanks for all!
You can just omit the parameter names:
int main(int, char *[])
{
return 0;
}
And in the case of main, you can even omit the parameters altogether:
int main()
{
// no return implies return 0;
}
See "§ 3.6 Start and Termination" in the C++11 Standard.
There is the <tuple> in C++11, which includes the ready to use std::ignore object, that's allow us to write (very likely without imposing runtime overheads):
void f(int x)
{
std::ignore = x;
}
I have used a function with an empty body for that purpose:
template <typename T>
void ignore(T &&)
{ }
void f(int a, int b)
{
ignore(a);
ignore(b);
return;
}
I expect any serious compiler to optimize the function call away and it silences warnings for me.
To "disable" this warning, the best is to avoid writing the argument, just write the type.
void function( int, int )
{
}
or if you prefer, comment it out:
void function( int /*a*/, int /*b*/ )
{
}
You can mix named and unnamed arguments:
void function( int a, int /*b*/ )
{
}
With C++17 you have [[maybe_unused]] attribute specifier, like:
void function( [[maybe_unused]] int a, [[maybe_unused]] int b )
{
}
Nothing equivalent, no.
So you're stuck with the same old options. Are you happy to omit the names in the parameter list entirely?
int main(int, char**)
In the specific case of main, of course, you could simply omit the parameters themselves:
int main()
There are also the typical implementation-specific tricks, such as GCC's __attribute__((unused)).
What do you have against the old and standard way?
void f(int a, int b)
{
(void)a;
(void)b;
return;
}
Macros may not be ideal, but they do a good job for this particular purpose. I'd say stick to using the macro.
The Boost header <boost/core/ignore_unused.hpp> (Boost >= 1.56) defines, for this purpose, the function template boost::ignore_unused().
int fun(int foo, int bar)
{
boost::ignore_unused(bar);
#ifdef ENABLE_DEBUG_OUTPUT
if (foo < bar)
std::cerr << "warning! foo < bar";
#endif
return foo + 2;
}
PS C++17 has the [[maybe_unused]] attribute to suppresses warnings on unused entities.
There's nothing new available.
What works best for me is to comment out the parameter name in the implementation. That way, you get rid of the warning, but still retain some notion of what the parameter is (since the name is available).
Your macro (and every other cast-to-void approach) has the downside that you can actually use the parameter after using the macro. This can make code harder to maintain.
I really like using macros for this, because it allows you better control when you have different debug builds (e.g. if you want to build with asserts enabled):
#if defined(ENABLE_ASSERTS)
#define MY_ASSERT(x) assert(x)
#else
#define MY_ASSERT(x)
#end
#define MY_UNUSED(x)
#if defined(ENABLE_ASSERTS)
#define MY_USED_FOR_ASSERTS(x) x
#else
#define MY_USED_FOR_ASSERTS(x) MY_UNUSED(x)
#end
and then use it like:
int myFunc(int myInt, float MY_USED_FOR_ASSERTS(myFloat), char MY_UNUSED(myChar))
{
MY_ASSERT(myChar < 12.0f);
return myInt;
}
I have my own implementation for time critical segments of code.
I've been researching a while a time critical code for slow down and have found this implementation consumes about 2% from the time critical code i have being optimized:
#define UTILITY_UNUSED(exp) (void)(exp)
#define UTILITY_UNUSED2(e0, e1) UTILITY_UNUSED(e0); UTILITY_UNUSED(e1)
#define ASSERT_EQ(v1, v2) { UTILITY_UNUSED2(v1, v2); } (void)0
The time critical code has used the ASSERT* definitions for debug purposes, but in release it clearly has cutted out, but... Seems this one produces a bit faster code in Visual Studio 2015 Update 3:
#define UTILITY_UNUSED(exp) (void)(false ? (false ? ((void)(exp)) : (void)0) : (void)0)
#define UTILITY_UNUSED2(e0, e1) (void)(false ? (false ? ((void)(e0), (void)(e1)) : (void)0) : (void)0)
The reason is in double false ? expression. It somehow produces a bit faster code in release with maximal optimization.
I don't know why this is faster (seems a bug in compiler optimization), but it at least a better solution for that case of code.
Note:
Most important thing here is that a time critical code slow downs without above assertions or unused macroses in release. In another words the double false ? expression surprisingly helps to optimize a code.
windows.h defines UNREFERENCED_PARAMETER:
#define UNREFERENCED_PARAMETER(P) {(P) = (P);}
So you could do it like this:
#include <windows.h>
#include <stdio.h>
int main(int argc, char **argv) {
UNREFERENCED_PARAMETER(argc);
puts(argv[1]);
return 0;
}
Or outside of Windows:
#include <stdio.h>
#define UNREFERENCED_PARAMETER(P) {(P) = (P);}
int main(int argc, char **argv) {
UNREFERENCED_PARAMETER(argc);
puts(argv[1]);
return 0;
}
I have succesfully using dyld -macosx- to interpose standard C functions to a third party application, getting important information about the workarounds it does. But what I really need is to replace a certain function of a certain class.
The function I want to override is QString::append(..., ..., ...), so each time a string is appended to another -the whole application uses qstring-, i find out.
Is there a way? Here's the code I already have.
// libinterposers.c
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <dlfcn.h>
#include <stdlib.h>
typedef struct interpose_s {
void *new_func;
void *orig_func;
} interpose_t;
int my_open(const char *, int, mode_t);
int my_close(int);
void* my_malloc(size_t);
static const interpose_t interposers[] \
__attribute__ ((section("__DATA, __interpose"))) = {
{ (void *)my_open, (void *)open },
{ (void *)my_close, (void *)close },
{ (void *)my_malloc, (void *)malloc },
};
int
my_open(const char *path, int flags, mode_t mode)
{
int ret = open(path, flags, mode);
printf("--> %d = open(%s, %x, %x)\n", ret, path, flags, mode);
return ret;
}
int
my_close(int d)
{
int ret = close(d);
printf("--> %d = close(%d)\n", ret, d);
return ret;
}
void*
my_malloc(size_t size)
{
void *ret = malloc(size);
//fprintf(stderr, "Reserva de memoria");
return ret;
}
Thank you very much
C++ does name mangling. This means member function QString::mid() looks something like __ZNK7QString3midEii to the linker. Run the nm(1) command on the library you are interposing on to see the symbols.
It is going to be much easier that this. QString uses memcpy to concatenate and work with Strings, and I can easily override memcpy, and apply a regular expression to the result, logging only the strings I want. Piece of cake. No need for magic voodo-hoodo :)