I am trying to make a nested function call with a variable number of parameters without retrieving them. And I get the wrong result.
Here is my simple c++ program:
extern "C" {
#include <stdio.h>
}
#include <cstdarg>
class ctty {
public:
ctty();
int logger(int prior, const char* format, ...);
private:
};
ctty::ctty(){};
int ctty::logger(int prior, const char* format, ...)
{
va_list ap;
va_start(ap,format);
printf(format, ap);
va_end(ap);
return 0;
}
int main(int argc, char** argv)
{
ctty tty;
tty.logger(0, "Test %d %d %d\n", 7, 5, 5);
return 0;
}
result:
Test -205200 7 5
I expect a result
Test 7 5 5
I don’t understand what am I doing wrong?
Thanks in advance.
You can't directly pass va_list to printf. va_list is a wrapper around the actual list of arguments (whatever its representation is). Although C way to do should be to use vprintf, in C++ there are safer alternatives, like variadic templates allowing to create a safer version of formatted printing, e.g. (a fictional format string for brevity of example):
#include <iostream>
#include <cstdlib>
class ctty {
public:
ctty();
template<typename T, typename... Args>
int logger(int prior, const char* format, T value, Args... args);
private:
void logger(int prior, const char *s);
};
ctty::ctty(){};
void ctty::logger(int prior, const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
int ctty::logger(int prior, const char* format, T value, Args... args)
{
while (*format) {
if (*format == '%') {
std::cout << value;
logger(prior, format + 1, args...);
return 0;
}
std::cout << *format++;
}
throw std::logic_error("extra arguments provided to logger");
}
int main(int argc, char** argv)
{
ctty tty;
tty.logger(0, "Test % % %\n", 7.55f, "Tada!", 888);
return 0;
}
This part of your code:
extern "C" {
#include <stdio.h>
}
Is technically an undefined behavior, while it may compile and not have adverse effect in some cases, it's not portable. You have to use C++ headers, e.g. <cstdio>
Related
I have a number of specialized templates which look like this:
template <unsigned long long>
Result<unsigned long long> strToNumber (const std::string& str)
{
std::string cleanStr;
//processing 'str' here...
return strtoull(cleanStr.c_str(), NULL, 10);
}
When I call this function using
auto idResult = Util::str::strToNumber<unsigned long long>(std::string(idFromDB["id"].GetString()));
I get the following error:
RequestManager.cpp:30:128: error: no matching function for call to ‘strToNumber(std::__cxx11::string)’
igned long long> idResult = Util::str::strToNumber<unsigned long long>(std::string(dataFromDB["id"].GetString()));
What am I doing wrong?
It seems that what you are trying to write is something line this:
#include <string>
template <typename Result>
Result strToNumber (const std::string& str)
{
std::string cleanStr;
//processing 'str' here...
return std::strtoull(cleanStr.c_str(), nullptr, 10);
}
int main(int argc, char *argv[])
{
auto idResult = strToNumber<unsigned long long>("123456789");
return 0;
}
But since std::strtoull() returns unsigned long long the template form of the function could lead to problems if someone calls strToNumber<int>("-1").
So that would suggest your code should be similar to:
#include <string>
unsigned long long strToNumber (const std::string& str)
{
std::string cleanStr;
//processing 'str' here...
return std::strtoull(cleanStr.c_str(), nullptr, 10);
}
int main(int argc, char *argv[])
{
auto idResult = strToNumber("123456789");
return 0;
}
But if you really want to use the flexibility of static polymorphic code you could look at something like:
#include <string>
#include <type_traits>
template <typename Result>
Result strToNumber (const std::string& str)
{
std::string cleanStr;
//processing 'str' here...
constexpr bool u_long_long = std::is_same_v<unsigned long long, Result>;
constexpr bool u_long = std::is_same_v<unsigned long, Result>;
static_assert(u_long_long || u_long, "Requested type not supported.");
if constexpr (u_long_long) {
return std::strtoull(cleanStr.c_str(), nullptr, 10);
}
if constexpr (u_long) {
return std::strtoul(cleanStr.c_str(), nullptr, 10);
}
}
int main(int argc, char *argv[])
{
auto idResult = strToNumber<unsigned long long>("123456789");
return 0;
}
I am trying to write a macro for logging mechanism. I wrote a variadic macro but it does not work with std::string. The code looks like the following:
#include <stdio.h>
#include <string>
#define LOG_NOTE(m, ...) printf(m, ##__VA_ARGS__)
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s", "Hello World", bar, foo);
return 0;
}
If I would call the macro like following, I would not get any error.
LOG_NOTE("%s %d %s", "Hello World", bar, "random string");
Compiler Output:
In function 'int main()': 5:49: error: cannot pass objects of
non-trivially-copyable type 'std::string {aka class
std::basic_string}' through '...' 11:5: note: in expansion of
macro 'LOG_NOTE'
The issue here is not the variadic macro, but the call to printf. Have a look at the documentation: the format specifier "%s" corresponds to char*, not std::string. printf can only handle primitive builtin types. You can change you invocation to
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
to fix this.
I wrote a variadic macro
Don't. Use a variadic template function.
The actual problem you have is that you're trying to pass a C++ object (std::string) through a C API (printf). This is not possible.
You'd need some mechanism for conversion, for example:
#include <stdio.h>
#include <string>
template<class T>
decltype(auto) convert_for_log_note(T const& x)
{
return x;
}
decltype(auto) convert_for_log_note(std::string const& x)
{
return x.c_str();
}
template<class...Args>
void LOG_NOTE(const char* format, Args&&...args)
{
printf(format, convert_for_log_note(args)...);
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
return 0;
}
Example output:
Hello World 5 random string
http://coliru.stacked-crooked.com/a/beb3431114833860
Update:
For C++11 you'll need to spell out the return types by hand:
#include <stdio.h>
#include <string>
template<class T>
T const& convert_for_log_note(T const& x)
{
return x;
}
const char* convert_for_log_note(std::string const& x)
{
return x.c_str();
}
template<class...Args>
void LOG_NOTE(const char* format, Args&&...args)
{
printf(format, convert_for_log_note(args)...);
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
return 0;
}
You cannot pass object to printf, so you have currently to use
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
If you don't need formatting, and just write every argument separated with space, you might simply use variadic template instead of MACRO:
template <typename ... Ts>
void LOG_NOTE(const Ts&...args)
{
const char* sep = "";
(((std::cout << sep << args), sep = " "), ...); // C++17 folding expression
// C++11/C++14 version are more verbose:
// int dummy[] = {0, ((std::cout << sep << args), (sep = " "), 0)...};
// static_cast<void>(dummy); // avoid warning for unused variable
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("Hello World", bar, foo);
}
Demo
I could not get #richardhodges nice solution to work in any of the C++11 compilers I tried. However, the following works with gcc -std=c++11:
#include <stdio.h>
#include <string>
template<class T>
T convert_for_log_note(T const& x)
{
return x;
}
inline const char* convert_for_log_note(std::string const& x)
{
return x.c_str();
}
template<class...Args>
void LOG_NOTE(const char* format, Args&&...args)
{
printf(format, convert_for_log_note(args)...);
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
return 0;
}
The inline keyword is necessary with the above solution for the Arduino C++ compiler, whereas other g++ compilers do not require it (the ones I have tried, anyways). Without this keyword, the Arduino code compiles, but the linker complains about multiple definitions.
Good evening,
I'm writing some classes, and I have a case where I would like a class method to take a variable number of arguments. I had issues getting this to compile, so I set up a very simple test case:
TestObject.h:
#include <ostream>
#include <stdarg.h>
class TestObject
{
public:
void test(int intCount, ...);
};
and TestObject.cpp:
#include "TestObject.h"
void TestObject::test(int intCount, ...)
{
va_list objList;
va_start objStart(objList, intCount);
int numArgs = va_arg(objList, int);
}
Finally, main.cpp:
#include<stdarg.h>
int maxof(int n_args, ...)
{
va_list ap;
va_start(ap, n_args);
int max = va_arg(ap, int);
for (int i = 2; i <= n_args; i++) {
int a = va_arg(ap, int);
if (a > max) max = a;
}
va_end(ap);
return max;
}
int main(int argc, char** argv)
{
int max = maxof(3, 1, 3, 2);
return 0;
}
main.cpp will compile just fine on its own, so I've narrowed it down to the case where the variable-length argument is in a class. When I add the classes to the project and try to compile, the build fails with the error C2065: '__crt_va_start': undeclared identifier. I'm building in Visual Studio 2015.
I'm aware that C++ has variadic templates, but whenever I get into template classes, I always seem to have to convert the class and usually related classes into header-only files and I'd like to avoid that. What I would probably do alternatively is just take a vector argument of some generic type.
Can someone tell me if I'm missing something here? Should I be able to compile and run a class method that has a variable-length argument in C++, or is this just something that's not supported?
Thanks!
Using a modern compiler you should not fall back to this old-style ellipsis syntax. For your purposes std::initializer_list is pretty good an alternative.
Look how simple the code with it is:
#include <limits>
int maxof(std::initializer_list<int> args)
{
int max = std::numeric_limits<int>::min();
for(auto arg : args)
if(arg > max) max = arg;
return max;
}
int main(int argc, char** argv)
{
int max = maxof({3, 7, 1, 3, 2, 5});
return 0;
}
I am using libxml2 to validate an xml file against an xsd schema.
Using xmlSchemaSetParserErrors function, errors are output to stderr.
I need to get these validation errors, store them in memory and display to the user.
How can I redirect these errors ? Could you provide me some examples ?
Thanks,
Andrea
This example uses the validation callback mechanism of the parser module. The signature of callbacks expected by xmlSchemaSetParserErrors seems to be the same.
#include <iostream>
#include <cstdarg>
#include <cstdio>
#include <vector>
#include <string>
#include <iterator>
#include <libxml/parser.h>
#include <libxml/tree.h>
struct ParserContext
{
ParserContext() : context(xmlNewParserCtxt()) {}
~ParserContext() { xmlFreeParserCtxt(context); }
xmlParserCtxtPtr context;
private:
ParserContext(ParserContext&);
void operator=(ParserContext&);
};
struct ErrorHandler
{
std::vector<std::string> errors;
void RegisterErrorHandling(xmlValidCtxt& validationContext)
{
// Change this to register for schema errors...
validationContext.userData = this;
validationContext.error = &ErrorHandler::Handle;
}
private:
static void Handle(void *handler, const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
std::string message = MakeMessage(format, arguments);
va_end(arguments);
ErrorHandler* errorHandler = static_cast<ErrorHandler*>(handler);
errorHandler->errors.push_back(message);
}
static std::string MakeMessage(const char* format, va_list arguments)
{
const size_t bufferSize = 200;
std::vector<char> buffer(bufferSize, 0);
size_t charactersWritten =
vsnprintf(&buffer.front(), bufferSize, format, arguments);
if (charactersWritten == -1)
buffer.back() = 0; // Message truncated!
return std::string(&buffer.front());
}
};
struct XmlDocument
{
static XmlDocument FromFile(const char* fileName)
{
ParserContext parser;
ErrorHandler errorHandler;
errorHandler.RegisterErrorHandling(parser.context->vctxt);
XmlDocument document(xmlCtxtReadFile(
parser.context, fileName, NULL, XML_PARSE_DTDVALID));
document.errors = move(errorHandler.errors);
return document;
}
XmlDocument(XmlDocument&& other) :
xmlPointer(other.xmlPointer),
errors(move(other.errors))
{
other.xmlPointer = nullptr;
}
~XmlDocument()
{
xmlFreeDoc(xmlPointer);
}
xmlDocPtr xmlPointer;
std::vector<std::string> errors;
private:
XmlDocument(xmlDocPtr pointer) : xmlPointer(pointer) {}
XmlDocument(XmlDocument&);
void operator=(XmlDocument&);
};
void DisplayErrorsToUser(
const XmlDocument& document,
std::ostream& displayStream = std::cout)
{
using namespace std;
copy(begin(document.errors), end(document.errors),
ostream_iterator<string>(displayStream, "\n"));
}
int main()
{
auto xml = XmlDocument::FromFile("test.xml");
DisplayErrorsToUser(xml);
}
Or even more concise:
void err(void *ctx, const char *msg, ...)
{
va_list args;
va_start(args, msg);
char *val = va_arg(args,char*);
va_end(args);
}
val now contains validation errors
As said in the API documentation, xmlSchemaSetParserErrors() Set the callback functions used to handle errors for a validation context.
You need to write two callback functions with respect to defined signature :
xmlSchemaValidityErrorFunc err
xmlSchemaValidityWarningFunc warn
An example could be :
void err(void *ctx, const char *msg, ...)
{
char buf[1024];
va_list args;
va_start(args, msg);
int len = vsnprintf_s(buf, sizeof(buf), sizeof(buf)/sizeof(buf[0]), msg, args);
va_end(args);
if(len==0) // Can't create schema validity error!
else // Do something to store `buf`,
// you may need to use void *ctx to achieve this
return;
}
Then just call
xmlSchemaSetValidErrors(valid_ctxt_ptr, (xmlSchemaValidityErrorFunc) err, (xmlSchemaValidityWarningFunc) warn, ctx);
before calling
xmlSchemaValidateDoc()
I am looking to do this in C/C++. I came across Variable Length Arguments, but this suggests a solution with Python and C using libffi.
Now, if I want to wrap the printf function with myprintf.
I do it like below:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
printf(fmt, args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n", a, v, b);
return 0;
}
But the results are not as expected!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
What did I miss?
The problem is that you cannot use 'printf' with va_args. You must use vprintf if you are using variable argument lists. vprint, vsprintf, vfprintf, etc. (there are also 'safe' versions in Microsoft's C runtime that will prevent buffer overruns, etc.)
You sample works as follows:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n", a, v, b);
return 0;
}
In C++11, this is one possible solution using variadic templates:
template<typename... Args>
void myprintf(const char* fmt, Args... args)
{
std::printf(fmt, args...);
}
As rubenvb points out, there are trade-offs to consider. For example, you will be generating code for each instance which will lead to code bloat.
I am also unsure what you mean by pure.
In C++ we use:
#include <cstdarg>
#include <cstdio>
class Foo
{
void Write(const char* pMsg, ...);
};
void Foo::Write( const char* pMsg, ...)
{
char buffer[4096];
std::va_list arg;
va_start(arg, pMsg);
std::vsnprintf(buffer, 4096, pMsg, arg);
va_end(arg);
...
}
Actually, there's a way to call a function that doesn’t have a va_list version from a wrapper. The idea is to use assembler, do not touch arguments on the stack, and temporarily replace the function return address.
An example for Visual C x86. call addr_printf calls printf():
__declspec( thread ) static void* _tls_ret;
static void __stdcall saveret(void *retaddr) {
_tls_ret = retaddr;
}
static void* __stdcall _getret() {
return _tls_ret;
}
__declspec(naked)
static void __stdcall restret_and_return_int(int retval) {
__asm {
call _getret
mov [esp], eax ; /* replace current retaddr with saved */
mov eax, [esp+4] ; /* retval */
ret 4
}
}
static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) {
printf("calling printf(\"%s\")\n", fmt);
}
static void __stdcall _dbg_printf_end(int ret) {
printf("printf() returned %d\n", ret);
}
__declspec(naked)
int dbg_printf(const char *fmt, ...)
{
static const void *addr_printf = printf;
/* prolog */
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
nop
}
{
va_list args;
va_start(args, fmt);
_dbg_printf_beg(fmt, args);
va_end(args);
}
/* epilog */
__asm {
mov esp, ebp
pop ebp
}
__asm {
call saveret
call addr_printf
push eax
push eax
call _dbg_printf_end
call restret_and_return_int
}
}
Are you using C or C++? The next C++ version, C++0x, will support variadic templates which provide a solution to that problem.
Another workaround can be achieved by clever operator overloading to achieve a syntax like this:
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
In order to get this to work, the class varargs has to be implemented to override operator = that returns a proxy object which, in turn, overrides operator ,. However, making this variant type safe in current C++ isn't possible as far as I know since it would have to work by type erasure.
How do you mean a pure C/C++ solution?
The rest parameter (...) is supported cross platform in the C runtime.
va_arg, va_copy, va_end, va_start
void myprintf(char* fmt, ...)
{
va_ list args;
va_ start(args, fmt);
printf(fmt, args); // This is the fault. "vprintf(fmt, args);"
// should have been used.
va_ end(args);
}
If you're just trying to call printf,
there's a printf variant called vprintf that takes
the va_list directly: vprintf(fmt, args);