闲暇的产物:用boost::spirit写的sprintf-like函数

qingcairousi 2006-10-02 07:23:58
最近有点时间,看了下boost::spirit,这个库很有趣,可以直接用c++写EBNF。试着用它写了一个sprintf,内部是利用stringstream格式化,效率什么的都没有考虑,主要目的就是用boost::spirit写printf格式化字符串的EBNF。函数粗略的测试过,并没有支持全部的功能(hh,l之类的长度操作就没有支持,嫌麻烦),EBNF也不是那么严谨,但普通的格式化还是可以的:)


#include <string>
#include <sstream>
#include <iomanip>
#include <stdarg.h>
#include "boost/spirit.hpp"
#include "boost/function.hpp"
#include "boost/bind.hpp"

using namespace std;
using namespace boost;
using namespace boost::spirit;

struct Formatter
{
enum Flags
{
SHARP,
MINUS,
SPACE,
PLUS,
NONE
};
stringstream formatStream;
ios::fmtflags oldFlags;
Flags flag;
va_list& argsList;
Formatter(va_list& args):argsList(args)
{
oldFlags=formatStream.flags();
}
void onChar(const char& ch)
{
formatStream<<ch;
}
void onPINT(const char* const begin,const char* const end)
{
int* pi=va_arg(argsList,int*);
formatStream<<*pi;
}
void onPOINTER(const char* const begin,const char* const end)
{
void* ui=va_arg(argsList,void*);
formatStream<<hex<<ui;
}
void onCSTRING(const char* const begin,const char* const end)
{
char* str=va_arg(argsList,char*);
formatStream<<str;
}
void onCHARACTER(const char* const begin,const char* const end)
{
formatStream.setf(ios::fixed);
int k=va_arg(argsList,int);
formatStream<<(char)k;
}
void onDOUBLE_HEX(const char* const begin,const char* const end)
{
formatStream.setf(ios::fixed);
double k=va_arg(argsList,double);
formatStream<<hex<<k;
}
void onDOUBLE_FIX(const char* const begin,const char* const end)
{
formatStream.setf(ios::fixed);
double k=va_arg(argsList,double);
formatStream<<k;
}
void onDOUBLE(const char* const begin,const char* const end)
{
formatStream.setf(ios::fixed);
double k=va_arg(argsList,double);
formatStream<<k;
}
void onDOUBLE_E(const char* const begin,const char* const end)
{
formatStream.setf(ios::scientific);
double k=va_arg(argsList,double);
formatStream<<k;
}
void onUINT_OCT(const char* const begin,const char* const end)
{
unsigned int k=va_arg(argsList,unsigned int);
formatStream<<oct<<k;
}
void onUINT_HEX(const char* const begin,const char* const end)
{
unsigned int k=va_arg(argsList,unsigned int);
formatStream<<hex<<k;
}
void onUINT_DEC(const char* const begin,const char* const end)
{
unsigned int k=va_arg(argsList,unsigned int);
formatStream<<dec<<k;
}
void onINTEGER(const char* const begin,const char* const end)
{
int k=va_arg(argsList,int);
formatStream<<dec<<k;
}
void onLENGTH(const char* const begin,const char* const end)
{
string len(begin,end);
///@todo implement me
}
void onPRECETION(int val)
{
formatStream.precision(val);
}
void onWIDTH(int val)
{
formatStream.width(val);
}
void onFLAG(const char* const begin,const char* const end)
{
switch(*begin)
{
case '#':
flag=SHARP;
formatStream.setf(ios::showbase);
break;
case '-':
flag=MINUS;
break;
case ' ':
flag=SPACE;
break;
case '+':
flag=PLUS;
break;
case '0':
formatStream<<setfill('0');
default:
break;
}
}
void onCONTENT(const char* const begin,const char* const end)
{
flag=NONE;
}
void onFMT_STR(const char* const begin,const char* const end)
{
}
void onFMTStart(const char ch)
{
flag=NONE;
formatStream.flags(oldFlags);
formatStream.width(0);
formatStream.precision(6);
formatStream<<setfill(' ');
}
};

inline string sprintf(const char* fmt,...)
{
//EBNF of sprintf's format
va_list arg_ptr;
va_start(arg_ptr,fmt);
Formatter fo(arg_ptr);
rule<> PINT=ch_p('n');
rule<> POINTER=ch_p('p');
rule<> CSTRING=ch_p('s');
rule<> CHARACTER=ch_p('c');
rule<> DOUBLE_HEX=ch_p('a')|'A';
rule<> DOUBLE_FIX=ch_p('g')|'G';
rule<> DOUBLE=ch_p('f')|'F';
rule<> DOUBLE_E=ch_p('e')|'E';
rule<> UINT_OCT=ch_p('o');
rule<> UINT_HEX=ch_p('x')|'X';
rule<> UINT_DEC=ch_p('u');
rule<> UINT=
UINT_DEC[bind(&Formatter::onUINT_DEC,ref(fo),_1,_2)] |
UINT_HEX[bind(&Formatter::onUINT_HEX,ref(fo),_1,_2)] |
UINT_OCT[bind(&Formatter::onUINT_OCT,ref(fo),_1,_2)];
rule<> INTEGER=ch_p('d')|'i';
rule<> TYPE_SPEC =
INTEGER[bind(&Formatter::onINTEGER,ref(fo),_1,_2)] |
UINT |
DOUBLE_HEX[bind(&Formatter::onDOUBLE_HEX,ref(fo),_1,_2)] |
DOUBLE_FIX[bind(&Formatter::onDOUBLE_FIX,ref(fo),_1,_2)] |
DOUBLE[bind(&Formatter::onDOUBLE,ref(fo),_1,_2)] |
DOUBLE_E[bind(&Formatter::onDOUBLE_E,ref(fo),_1,_2)] |
CHARACTER[bind(&Formatter::onCHARACTER,ref(fo),_1,_2)] |
CSTRING[bind(&Formatter::onCSTRING,ref(fo),_1,_2)] |
POINTER[bind(&Formatter::onPOINTER,ref(fo),_1,_2)] |
PINT[bind(&Formatter::onPINT,ref(fo),_1,_2)];
rule<> LENGTH=longest_d[chseq_p("hh")|"h"|"ll"|"l"|"L"|"q"|"j"|"z"|"t"];
rule<> PRECITION='.'>>int_p[bind(&Formatter::onPRECETION,ref(fo),_1)];
rule<> WIDTH=int_p[bind(&Formatter::onWIDTH,ref(fo),_1)];
rule<> FLAG=ch_p('#')|'-'|space_p|'+'|'0';
rule<> CONTENT=ch_p('%')[bind(&Formatter::onFMTStart,ref(fo),_1)]>>
*FLAG[bind(&Formatter::onFLAG,ref(fo),_1,_2)]>>
*WIDTH>>
*PRECITION>>
*LENGTH[bind(&Formatter::onLENGTH,ref(fo),_1,_2)]>>
TYPE_SPEC;
rule<> FMT_STR=*((~ch_p('%'))[bind(&Formatter::onChar,ref(fo),_1)]|CONTENT[bind(&Formatter::onCONTENT,ref(fo),_1,_2)]);
//parse string
parse(fmt,FMT_STR[bind(&Formatter::onFMT_STR,ref(fo),_1,_2)]);
va_end(arg_ptr);
return fo.formatStream.str();
}
...全文
583 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
qingcairousi 2007-04-05
  • 打赏
  • 举报
回复
五个月后,终于见到一个回帖的……感动……
我实际运用中也是用grammar的。
onFLAG里写的时候还没有看到lazy_parser这个部分,不然就不用这么麻烦了。
接触得越多,就越觉得Spirit实在是非常优雅的一个库。

现在我在翻译Spirit的用户手册,在我的blog上面,有空多多指正:)
zwvista 2007-03-30
  • 打赏
  • 举报
回复
提点建议:
最好用grammar来写EBNF,这样所有语法集中在一个类之内
过多的[bind(&Formatter::。。。可以用宏来简化
onFLAG里的switch应该可以避免,'#'之类的本来就应该在不同语法条目下
可以用pheonix库(约等于bind+lambda)来取代bind库

24,854

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 工具平台和程序库
社区管理员
  • 工具平台和程序库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧