3

First, there's no portable way to compute the length of va_list. Perhaps there's a way to do this in debug (not optimized) configuration of Visual C++?

Specifically I have a variadic function:

void MyVariadic( const char* format, ... )
{
}

(and I cannot change the signature) and I want to detect cases where format contains percentage characters and the arguments list is empty (which likely means that someone passed a whatever string directly in place of format instead of using %s) and once I detect such cases I can assert() or something.

Is there a way to do this in debug non-optimized build in Visual C++?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Can you _add_ another signature? `void MyVariadic(const char* plain)` is a better match when only one string is provided. – MSalters Feb 20 '14 at 08:44
  • @MSalters: Perhaps that would do, but will overload resolution properly handle this? I'd also want to forward the call to the variadic version - is that possible? – sharptooth Feb 20 '14 at 08:57
  • Yes, overload resolution will prefer a non-vararg function over a vararg function. That makes forwarding a bit tricky, but here you can forward to `MyVariadic("%s", plain)` and ignore that trickery. – MSalters Feb 20 '14 at 10:43
  • @MSalters: I think that's just great, especially this way to make the forwarding. Could you please add this as an answer? – sharptooth Feb 20 '14 at 10:48
  • In C++11, You may use `template void MyVariadic(const char* format, Args&&...args)` – Jarod42 Feb 20 '14 at 11:57

3 Answers3

4

Since you are okay with Visual C++-specific solutions and if your goal is to help detect mismatched printf-style format strings, consider using SAL Annotations. A build that you run with code analysis turned on will report errors if the format string and the arguments don't match up.

The annotation you want is _Printf_format_string_. Take a look at how printf is annotated in stdio.h for inspiration.

Consider the following code:

int i = 12;
printf("%d %s", i);

Visual C++ 2013 run with code analysis reports for me

C6063 Missing string argument to 'printf' that corresponds to specifier '2'.

chwarr
  • 6,777
  • 1
  • 30
  • 57
  • Well, this won't really work in my case - I want to detect strings that are not yet known in compile time. Such as someone writes `MyVariadic(someStringVariable)` and `someStringVariable` is set to a string with percentage signs during runtime. – sharptooth Feb 20 '14 at 08:11
2

(From comments)

The problem is basically in calls like MyVariadic(str) where str is a const char[] known only at runtime. We can't change the signature of void MyVariadic(const char*, ...) but we can add an overload. void MyVariadic(const char*) is a better match.

From void MyVariadic(const char* str) it's hard to forward to void MyVariadic(const char*, ...). The generic solution is to cast &MyVariadic to void (*p)(const char*, ...) but in this particular case const char* is a printf-like format. That means we can forward to MyVariadic("%s", str).

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • I actually tried this with VC++ and gcc (on ideone.com) and both think the call is ambiguous. – sharptooth Aug 04 '14 at 12:51
  • You're not calling the function from inside `main()`. Add this: `MyVariadic("s");` into `main()` and it no longer compiles. – sharptooth Aug 04 '14 at 14:32
  • @sharptooth: Oops, you're right. I'm clearly overlooking something because I was quite certain that unused varargs decreased overload viability. – MSalters Aug 04 '14 at 14:35
  • @sharptooth: [Fixed](http://ideone.com/NOsfjK) at the price of a dummy template argument. Signature is technically untouched. – MSalters Aug 04 '14 at 14:50
  • It's not necessary to put the dummy argument onto the variadic function - it can be just as well put onto the newly introduced non-variadic function. This solves the problem - the original function is untouched and there's no ambiguity. – sharptooth Aug 05 '14 at 06:39
0

One possible way is to use variadic macro (with hardcoded limit):

#define COUNT_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#define COUNT(...)   COUNT_N(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
// Warning: COUNT() return 1 (as COUNT(A)) :-/
#define ARG1(S, ...) S

And then do something like:

#define MyVariadic_And_Check(...) do { \
    Check(ARG1(__VA_ARGS__), COUNT(__VA_ARGS__) - 1); \
    MyVariadic(__VA_ARGS__); } while(false) 

With Check(const char* format, int count) a function to check parameter count.
Note: Since MyVariadic has at least const char* format, COUNT will give correct answer.

In C++11, you may use something like:

template <typename ... Args>
void MyVariadic_And_Check(const char* format, Args&&...args);

to verify both count and type.

Jarod42
  • 203,559
  • 14
  • 181
  • 302