0

I want to use sscanf to parse a long string ..
The data to be parsed will be stored in a struct whose members are all of the type time_t.
Unfortunately, there isn't any format string to mark a time_t so I'm just going to typecast all time_t * arguments as unsigned long long *, and because its a very long string with lots of arguments, typecasting each argument one by one will only mess my editor screen ..

So, I created a macro to simplify this:

#define typecast(type, ...) (type) __VA_ARGS__

And I invoked it like this:

sscanf(string, PARSE_STRING,
    typecast(unsigned long long *, /* my arguments */));

I though this would expand to:

sscanf(string, PARSE_STRING,
    (unsigned long long *) /* arg1 */, (unsigned long long *) /* arg2 */, /* and so on */);

But after inspecting with gcc -E, I found it expanded to this:

sscanf(string, PARSE_STRING,
    (unsigned long long *) /* arg1 */, /* arg2 */, /* and so on */));

How can I achieve my desired expansion functionality using variadic functions ?

a3f
  • 8,517
  • 1
  • 41
  • 46
Amr Ayman
  • 1,129
  • 1
  • 8
  • 24
  • You might get some inspiration [here](http://stackoverflow.com/questions/26582302/a-group-of-variadic-macros/26582594#26582594). – M Oehm Nov 20 '14 at 13:32
  • [This question](http://stackoverflow.com/questions/1872220/) also has some relevant answers. – uesp Nov 20 '14 at 13:36
  • Note that `time_t` is normally an signed integer type. If you're scanning, you have to be sure that the type is compatible with `time_t` — it is somewhat platform specific. – Jonathan Leffler Nov 20 '14 at 15:01
  • @JonathanLeffler: I didn't know `time_t` was signed because time is either zero or positive, but anyway, I'm certain that the data I'm reading will never have negative values. – Amr Ayman Nov 21 '14 at 06:50
  • Classically, time 0 is 1970-01-01 00:00:00 Z (or +00:00). The `mktime()` function returns `(time_t)-1` on error. With a 32-bit signed type (`long` on older 32-bit systems), the range of representable times is from 1901-12-13 (0x80000000) to 2038-01-18 (0x7FFFFFFF) roughly. That's why you'll sometimes see comments about the Y2038 crisis; 32-bit signed `time_t` will rollover then. Most systems now use 64-bit `time_t` to avoid that problem (and the Y10K problem, etc.). – Jonathan Leffler Nov 21 '14 at 06:57

2 Answers2

0

I have seen C preprocessor code looping over arguments of variadic macros and I wouldn't want it in my code. However, in your case, you can take the following solution. I am presenting macros which work for up to 4 arguments, you will surely be able to extend it to any constant.

#define typecast4(type, a1, a2, a3, a4, ...) ((type)a1), ((type)a2), ((type)a3), ((type)a4)
#define typecast(type, ...) typecast4(type, __VA_ARGS__, NULL, NULL, NULL, NULL)

This particular solution generates 4 arguments always. Note that, in your case, this does not matter because scanf accepts and safely ignores any arguments beyond the entered format. So, the following code compiles:

int main() {
  time_t x,y;
  scanf("%lld, %lld", typecast(long long *, &x, &y));
  ...
}

However, I am worrying about the cast from time_t * to long long *. It is written nowhere that time_t must be represented by long long. If it is represented by int on some platform the previous code will be buggy. The correct way to scan your values would be something like:

int main() {
  time_t x,y;
  long long lx, ly;
  scanf("%lld, %lld", &lx, &ly);
  x = lx;
  y = ly;
  ...
}

which is more correct and does not require any variadic macro.

Marian
  • 7,402
  • 2
  • 22
  • 34
0

use boost

#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define CAMMA(r, data, i, elem) BOOST_PP_COMMA_IF(i)elem
#define CAST(r, data, elem) (data)elem
#define typecast(type, ...) BOOST_PP_SEQ_FOR_EACH_I(CAMMA, , BOOST_PP_SEQ_TRANSFORM(CAST, type, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)))
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70