3

This question is similar with this, It seems in my version I need to write my own sizeof...

Suppose I have a struct Wrapper with definition

struct Wrapper{
    string s;
}

and I have a function, it accept a pack of Wrapper and printf them. Because printf take a pack of const char *, so I need to map this pack of Wrapper into a pack of const char *

template<typename ... Args>
void printf_wrapper(string format, Args&& ... args){
    printf(format.c_str(), /* #1: WHAT CAN I WRITE HERE */)
}

I heard a swallow function may help me with that, but what does it like, and how it functions?

Community
  • 1
  • 1
calvin
  • 2,125
  • 2
  • 21
  • 38
  • 3
    `printf(format.c_str(), wrapper_to_string(args).c_str()...);`? – ildjarn Mar 11 '17 at 06:55
  • With C++17 comes [*fold expressions*](http://en.cppreference.com/w/cpp/language/fold) which allows you to use standard C++ streams (see example in the reference) which will make it much safer. The `printf` function is very troublesome in C++, and of course have safety issue C++ streams doesn't have (think about user-inputted format strings (way to common unfortunately) and what they might do if the arguments doesn't match). – Some programmer dude Mar 11 '17 at 07:02
  • There are of course ways to handle output of parameter packs using streams before C++17 too. The example `tprintf` function [in this parameter pack reference](http://en.cppreference.com/w/cpp/language/parameter_pack) shows you one way. – Some programmer dude Mar 11 '17 at 07:05

2 Answers2

4

Using the wrapper you defined in the question, it's a matter of using this:

template<typename ... Args>
void printf_wrapper(std::string format, Args&& ... args){
    printf(format.c_str(), args.s.c_str()...);
}

As a minimal, working example:

#include <type_traits>
#include <cstdio>
#include <string>

struct Wrapper{
    std::string s;
};

template<typename... T>
constexpr bool areWrappers =
    std::is_same<
        std::integer_sequence<bool, true, std::is_same<T, Wrapper>::value...>,
        std::integer_sequence<bool, std::is_same<T, Wrapper>::value..., true>
    >::value;

template<typename ... Args>
void printf_wrapper(std::string format, Args&& ... args){
    static_assert(areWrappers<std::decay_t<Args>...>, "!");
    printf(format.c_str(), args.s.c_str()...);
}

int main() {
    printf_wrapper("%s %s", Wrapper{"foo"}, Wrapper{"bar"});
}

I added also the areWrappers utility to check that your Args are actually all Wrappers. If they are not, the solution above won't work, but the static_assert will help to get out of it a meaningful error message.


See it on wandbox.

skypjack
  • 49,335
  • 19
  • 95
  • 187
2

I suggest using a simple solution which is similar to @ildjarn's posted in the comment above.

If the Wrapper structure looks like this:

struct Wrapper {
    std::string hidden_str;
};

Then you might change the print function as below:

template<typename... Args>
void print(const std::string& format, Args const&... args) {
    printf(format.c_str(), args.hidden_str.c_str()...);
}

wandbox example

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67