3

Consider the following code:

template<typename T0>
void send( const std::string& func, const T0& t0 )
{
   std::ostringstream s;
   s << func << ": " << t0;
   sendMessage( s.str() );
}

template<typename T0, typename T1>
void send( const std::string& func, const T0& t0, const T1& t1 )
{
   std::ostringstream s;
   s << func << ": " << t0 << "," << t1;
   sendMessage( s.str() );
}

template<typename T0, typename T1, typename T2>
void send( const std::string& func, const T0& t0, const T1& t1, const T2& t2 )
{
   std::ostringstream s;
   s << func << ": " << t0 << "," << t1 << "," << t2;
   sendMessage( s.str() );
}

etc...

I need this up to something like 15 arguments.

I'm just wondering if it's possible to use Boost.Preprocessor to simplify this repetition, and how I would go about it. I looked through the docs but it's fairly confusing.

Gerald
  • 23,011
  • 10
  • 73
  • 102
  • You could use default arguments. One function with up to 15 arguments isn't nearly as bad as one for each number of arguments. I'm not sure what the best defaulted values would be, but `char` and `0` sound probable, seeing as how the stream is characters. – chris Aug 30 '12 at 00:17
  • Might work for this particular instance, but I have a lot of similar boilerplate that does things other than push the arguments to a stream. Hoping to find out how to use Boost.Preprocessor for this (if possible) so I could apply it to the other cases as well, just figured this was a simple enough example to demonstrate what I am trying to accomplish. – Gerald Aug 30 '12 at 01:21
  • 2
    Would variadic functions/templates work for you? – chris Aug 30 '12 at 02:00
  • Variadic functions won't work. Variadic templates would be perfect if Visual Studio supported them. – Gerald Aug 30 '12 at 02:26
  • That's very true. I was shocked how little new C++11 went into VS11. At least with variadic templates you can check the number of arguments: `static_assert(sizeof args... <= 15, "Must pass 15 or fewer arguments.");` – chris Aug 30 '12 at 02:28
  • Perhaps [this question](http://stackoverflow.com/questions/7683041/how-to-implement-variadic-template-with-pre-c0xvs2008) might be of some help? – chris Aug 30 '12 at 02:31

2 Answers2

5

boost.preprocessor can do this kind of stuff and you can have descent tutorial here: Intro to preprocessor metaprogramming

For your specific example here's the code I came up with (note: I just ran it under the preprocessor to check that the resulting code was what I would expect but did not try to compile the code itself).

This uses both the enum_params macro that helps generate list of attributes and local_iterate that allow to expand a macro with a range of argument. I have also a macro if to handle your special case with ": " vs "," for the first argument sent to your stream.

Overall when you start to understand how boost.preprocessor work searching for the feature you need in its reference manual becomes relatively easy and its a "reasonable" replacement until variadic templates are supported by most compilers. Do note though that it is very intensive at compilation time, so use with caution.

edit: While I did consider this as a general exercise with a relatively simple example I do agree though that in term of coding for this specific example using a pseudo stream class as suggested by synek317 would be a more flexible and "lightweight" solution. Again implementing such stream do not require you to do the specialization of all the operators and in fact boost again provide the IOStreams library to help you implement a class as a standard C++ stream (http://www.boost.org/doc/libs/1_52_0/libs/iostreams/doc/index.html)

#include <boost/preprocessor/iteration/local.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/control/if.hpp>

# ifndef MY_SEND_MAX
#  define MY_SEND_MAX 15
# endif

/*
 * macros called through BOOST_PP_REPEAT to generate lists
 * J : I don't know what it is
 * I : the current counter number
 * D : the 3rd argument of BOOST_PP_REPEAT
 */
// Generate the list of arguments after std::string
# define MY_SEND_PARAM(J,I,D) , BOOST_PP_CAT(const T,I) & BOOST_PP_CAT(arg,I)
// Generate the stream's <<s adter the string
# define MY_SEND_STREAM(J,I,D) <<BOOST_PP_IF(I,",",": ")<<BOOST_PP_CAT(arg,I)

/*
 * Macro to call for generating onde function
 * N : the number of arguments
 */
// Create one function with a string and N extra aarguments
# define MY_SEND(N) \           
  template<BOOST_PP_ENUM_PARAMS(N,typename T)>  \
  void send(std::string const &fn BOOST_PP_REPEAT(N,MY_SEND_PARAM,~)) { \
    std::ostringstream s; \
    s<<fn BOOST_PP_REPEAT(N,MY_SEND_STREAM,~); \
    sendMessage(s.str()); \
  }
// End of my macro

/*
 * BOOST_PP local loop to generate all the the function
 */
// What macro to call in the loop
#define BOOST_PP_LOCAL_MACRO(n)   MY_SEND(n)
// do the job form 0 to MY_SEND_MAX 
#define BOOST_PP_LOCAL_LIMITS     (0,MY_SEND_MAX)
// execute the loop
#include BOOST_PP_LOCAL_ITERATE()

// undefine all my stuff
# undef MY_SEND_PARAM
# undef MY_SEND_STREAM
# undef MY_SEND
# undef BOOST_PP_LOCAL_MACRO
# undef BOOST_PP_LOCAL_LIMITS
Frederic Py
  • 51
  • 1
  • 4
  • Thank you. I agree with the stream comment, and I did eventually go that route for this specific problem. I am actually working on another problem now that I think this will be useful for, though, so I will see if I can learn from your code how to use Boost.Preprocessor correctly. – Gerald Jan 30 '13 at 18:08
1

15 arguments sounds like really bad idea. Maybe you can create an object which will behave like stream? In your example, you can easily inherit from ostringstream and add method send() and I belive similar problems can be solved this way, maybe with operator<< overload.

Preprocessor is nice, but way to often hard to debug.

synek317
  • 749
  • 2
  • 7
  • 22
  • I thought about that, but it would quickly become annoying to maintain when having to constantly go through and add new << operators for every type I need to support whenever I add a new object to process them. And the processing would be much more complex because I would have to queue up the arguments somehow, most likely with some type of variant, and then sort out what was received after the last argument. If I need to debug, I can always have the preprocessor give me the full code and replace it in the source file while debugging. – Gerald Aug 30 '12 at 02:40
  • 1
    What is the difference between template send function and template operator? – synek317 Aug 30 '12 at 10:03