2

For some reason (ehm CUDA) I use printf() in my C++ code. I would like to be able to template around some of these uses - but for that to happen I need to obtain the printf type specifier for various types. Suppose I only need this to work for types which actually have relevant specifiers; and that I don't really care about scientific vs. decimal notation and other such details.

Is there an idiomatic way of doing this other than having a lookup table?

Note: I would like this to all happen at compile time, not run time. Despite printf() itself only parsing it at run-time.

eush77
  • 3,870
  • 1
  • 23
  • 30
einpoklum
  • 118,144
  • 57
  • 340
  • 684

1 Answers1

3

I would use something like this:

template <typename T> struct PrintfSpecifier;
#define TYPE_SPEC(type, spec) \
    template <> struct PrintfSpecifier<type>{static constexpr const char *value = spec;}
TYPE_SPEC(int          , "%d");
TYPE_SPEC(unsigned int , "%u");
// More types here...
#undef TYPE_SPEC

And to get your string you can use PrintfSpecifier<int>::value.


If you're able to use C++14 and you like fancy features, there is a more clean alternative:

template <typename T> constexpr const char *printf_specifier = "";
#define TYPE_SPEC(type, spec) \
    template <> constexpr const char *printf_specifier<type> = spec;
    TYPE_SPEC(int          , "%d");
TYPE_SPEC(unsigned int , "%u");
// More types here...
#undef TYPE_SPEC
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 2
    Do need to be a bit careful because those `const char *`s do not have linkage, so if you ever have a function that takes a const char* by reference and try to pass this, you get bizarre and unhelpful linker errors. – Nir Friedman Jul 10 '16 at 21:12
  • @NirFriedman Good point, but I don't think it's a problem. Const references work fine with it and it non-const references are not supposed to work with constants anyway. – HolyBlackCat Jul 10 '16 at 21:35
  • You're suggesting basically what I was about to do and hoping to avoid :-( Also, I'd make that macro DEFINE_PRINTF_SPECIFIER to reduce the chance of name collision; TYPE() is relatively likely to have already been defined by somebody. – einpoklum Jul 10 '16 at 21:53
  • @einpoklum Well, you could use fancy C++14 template variable to make the syntax even more clean. I don't know what else to suggest. Also, I think `T()` is also good option too. No one would dare to define such short macro name. :) – HolyBlackCat Jul 10 '16 at 22:00
  • @HolyBlackCat: I'd kind of assumed someone would say, "oh, just use `boost::esoteric_library::foo()`" or something. – einpoklum Jul 10 '16 at 22:05
  • @HolyBlackCat Are you sure about the const reference thing? I don't think that's the case: http://stackoverflow.com/questions/5391973/undefined-reference-to-static-const-int. – Nir Friedman Jul 10 '16 at 22:31
  • @HolyBlackCat I don't think that proves the case, the compiler could simply be optimizing it out and being lenient. If you look at the question I linked and the accepted answer, I'm pretty sure it is incorrect to pass it by const&. Unless there is something special about the fact that you are specializing templates, which doesn't seem to be the case but I'm not certain. – Nir Friedman Jul 10 '16 at 22:35
  • @NirFriedman Oops, you're right. I've tried it with different flags on my local machine and it works only with `-O2` or higher. – HolyBlackCat Jul 10 '16 at 22:43