使用Boost.Preprocessor减少代码重复

3
考虑以下代码:
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() );
}

我需要将其扩展到15个参数左右。

我想知道是否可以使用Boost.Preprocessor来简化这种重复,并且该如何操作。我已经查看了文档,但是感觉比较混乱难懂。


你可以使用默认参数。一个带有多达15个参数的函数并不像每个参数数量都有一个函数那样糟糕。我不确定最好的默认值是什么,但是char0听起来很可能,因为流是字符。 - chris
可能适用于这个特定的实例,但我有很多类似的样板代码,它们做的事情不仅仅是将参数推送到流中。希望找出如何使用Boost.Preprocessor(如果可能的话)来处理这个问题,这样我就可以将其应用到其他情况中,只是想举一个足够简单的例子来说明我想要实现的目标。 - Gerald
2
可变参数函数/模板对你有用吗? - chris
可变参数函数无法工作。如果Visual Studio支持它们,可变参数模板将是完美的选择。 - Gerald
这非常正确。我很震惊C++11中有多少新内容没有被加入VS11。至少使用可变参数模板,你可以检查参数个数:static_assert(sizeof args... <= 15, "必须传递15个或更少的参数。"); - chris
也许这个问题能够帮到你? - chris
2个回答

5

boost.preprocessor可以完成这种操作,您可以在此处找到详细的教程:预处理器元编程介绍

对于您特定的示例,这是我想出来的代码(注意:我只是在预处理器下运行它,以检查生成的代码是否符合我的预期,但没有尝试编译代码本身)。

这使用了enum_params宏,帮助生成属性列表,并使用local_iterate扩展具有一系列参数的宏。我还有一个宏用于处理您发送到流的第一个参数的特殊情况,即“:”与“,”之间的区别。

总的来说,当您开始理解boost.preprocessor的工作原理时,在其参考手册中搜索所需功能变得相对容易,直到大多数编译器支持可变参数模板为止,它是一个“合理”的替代品。请注意,它在编译时非常密集,因此请谨慎使用。

编辑:虽然我考虑过这个作为一个相对简单的通用练习,但我同意,在编写针对此特定示例的代码时,使用synek317建议的伪流类将是一种更灵活和“轻量级”的解决方案。再次实现这样的流不需要您对所有运算符进行专门化处理,事实上,boost还提供了IOStreams库来帮助您将类实现为标准C ++流(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

谢谢。我同意流评论,并最终采用了这种方法来解决这个具体问题。现在我正在处理另一个问题,我认为这将很有用,所以我会看看你的代码,学习如何正确使用Boost.Preprocessor。 - Gerald

1

15个参数听起来像是一个非常糟糕的主意。也许你可以创建一个行为类似于流的对象?在你的例子中,你可以轻松地从ostringstream继承,并添加send()方法,我相信类似的问题可以通过这种方式解决,或者使用operator<<重载。

预处理器很好用,但往往很难调试。


我考虑过这个,但是每当我需要支持新的对象时,不得不不断地添加新的<<运算符来维护它会很快变得令人烦恼。而且处理过程会更加复杂,因为我必须以某种方式排队参数,很可能是使用某种类型的变量,然后在最后一个参数之后解决接收到的内容。如果我需要调试,我总是可以让预处理器给我完整的代码,并在调试时替换源文件中的代码。 - Gerald
1
模板发送函数和模板运算符有什么区别? - synek317

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接