请教大牛!如何封装sprintf?很急!!

时间:2022-12-14 18:10:29
工作中遇到的问题,实现mysprintf对sprintf的封装
要求mysprintf的参数列表必须与sprintf一致,即int mysprintf(char *, const char *, ...)
也就是原有代码中对sprintf的调用可直接用mysprintf进行替换
必须强调一点(请大家注意审题):不是自己写一个函数来sprintf的功能,而是对其封装,即
int mysprintf(char *, const char *, ...)
{
    // do something
    sprintf(,,...); // 关键就是这里,如何解析mysprintf的传入参数并传给sprintf,这包括个数、类型等待
    // do something
    return len;
}
小弟在此谢谢了!

24 个解决方案

#1


应该用 vsprintf

#2


感谢#1朋友,谢谢!
事实上,我在描述问题时进行了类比简化。我的实际问题跟sprintf相似,于是我就用它做了类比。我的实际问题是,第三方提供的DLL的接口函数的参数中含有省略号,而我要对其封装调用。这个DLL属于工控方面的,函数实现向PCI总线发送一些指令,所以显然没法用vsprintf

#3


所以,这个问题的一般性描述就是:如何对含有省略号参数的函数进行封装?(封装它,而不是寻找它的替代物)

#4


引用 3 楼 ylyan20xx 的回复:
所以,这个问题的一般性描述就是:如何对含有省略号参数的函数进行封装?(封装它,而不是寻找它的替代物)

如果编译器支持的话,用变长函数模板
不行的话,就用宏吧。其他方法都有限制,不好搞。

#5


直接封装sprintf必须自己解释sFormat参数中的参数个数和类型,否则无法提取参数,然后估计得用汇编重建sprintf的参数表,然后再用汇编调用sprintf

#6


msdn:
va_arg, va_end, va_start
//
int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /* Initialize variable arguments. */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}

#7


引用 6 楼 schlafenhamster 的回复:
msdn:
va_arg, va_end, va_start
//
int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /* Initialize variable arguments. */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}


使用特定值(-1)作为可变参数参数表结束,在很多场合不适用,特别是参数类型不一致的时候更是如此

#8


...不定参数不能多级传递. 只有特殊的函数才接收这个外部传递的...参数.

给你个参考:
//注意, ... 可变参数不能传再次传递, 只有v开头的几个特殊函数可以处理.
void _TraceT(LPCTSTR lpszFmt, ...)
{
va_list args;
va_start(args, lpszFmt);
int len = _vsctprintf(lpszFmt, args)+1;
TCHAR *lpszBuf = (TCHAR*)_alloca(len*sizeof(TCHAR));//栈中分配, 不需要释放
_vstprintf_s(lpszBuf, len, lpszFmt, args);
va_end(args);
OutputDebugString(lpszBuf);


}

#9


" int first"

可以代入: "%d%d%d..."

#10


感谢以上好心人!
average的例子我也看过,但它不比sprintf,再者,我的问题是对含有省略号参数的函数进行直接封装,而不是寻找它的替代物
回楼上:你讲到“不定参数不能多级传递”,那是否意味着我的问题(对含有省略号参数的函数进行直接封装)根本就不成立?

#11


vsprintf 其实内部是能取得第一个变量的地址,根据地址来取值的。如果你封装 sprintf 则你也得想办法把地址传给它,但实际上 sprintf 只接受值,而不能传递 "..." 处第一个参数的地址进去。

#12


引用 10 楼 ylyan20xx 的回复:
感谢以上好心人!
average的例子我也看过,但它不比sprintf,再者,我的问题是对含有省略号参数的函数进行直接封装,而不是寻找它的替代物
回楼上:你讲到“不定参数不能多级传递”,那是否意味着我的问题(对含有省略号参数的函数进行直接封装)根本就不成立?

//变长模板参数,需要C++11支持
template<class ... T>
int func( T... v )
{
puts("------");
int r = printf( v... );
puts("======");
return r;
}

//变长参数宏,不一定所有编译器都支持
#define FUNC( ... )  \
{ \
puts("macro ------"); \
printf( __VA_ARGS__ ); \
puts("macro ======"); \
}


int main()
{
func( "%d,%f,%c\n" , 1,2.0 , 'c' );
FUNC( "%d,%f,%c\n" , 1,2.0 , 'c' );
    return 0;
}

其他方案都有缺陷...

#13


引用 12 楼 akirya 的回复:

//变长模板参数,需要C++11支持
template<class ... T>
int func( T... v )
{
puts("------");
int r = printf( v... );
puts("======");
return r;
}

//变长参数宏,不一定所有编译器都支持
#define FUNC( ... )  \
{ \
puts("macro ------"); \
printf( __VA_ARGS__ ); \
puts("macro ======"); \
}


int main()
{
func( "%d,%f,%c\n" , 1,2.0 , 'c' );
FUNC( "%d,%f,%c\n" , 1,2.0 , 'c' );
    return 0;
}

赞一个  请教大牛!如何封装sprintf?很急!!

#14


非常感谢大家!
回#12大牛:发帖时为了描述更具体,我用了sprintf,而我实际要封装的函数跟sprintf函数在功能上是完全不同的,它们仅仅是形式一样,即:都是参数列表中带有省略号。我要封装的是第三方提供的DLL里的一个导出函数。不知有没更一般性的方法?谢谢!

#15


如果楼主你细心, 已经发现有三种方案了. 

还管你是想要自己格式化串, 还是要遍历参数, 都已经给也方案了.
其它的, 还有什么问题吧.

或者你可以再详细的描述下你的问题, 最好是举几个例子, 你想做成什么效果.

#16


回楼上大牛:小弟功力浅显,不能确定你在#12贴的代码是否具有一般性
都怪我提到了sprintf,害了大家去往这个地方思考
我重新描述我的问题吧:
对第三方DLL的一个导出函数(其参数列表中带有省略号)进行封装,要求自己的函数具有与该导出函数一样的参数列表,并且我自己的函数必须去调用该导出函数
导出函数原型:int DLLInterface(char *, const char *, ...);
我的函数原型:int MyDLLInterface(char *, const char *, ...);
要求我的MyDLLInterface必须去调用DLLInterface
int MyDLLInterface(char *, const char *, ...)
{
    // do something
    DLLInterface(,,...); // 关键就是这里,我必须调用DLLInterface,参数如何传入?
    // do something
    return 0;
}
请大家抛开sprintf这个函数,我很后悔提到这个函数
谢谢!

#17


引用 16 楼 ylyan20xx 的回复:
回楼上大牛:小弟功力浅显,不能确定你在#12贴的代码是否具有一般性
都怪我提到了sprintf,害了大家去往这个地方思考
我重新描述我的问题吧:
对第三方DLL的一个导出函数(其参数列表中带有省略号)进行封装,要求自己的函数具有与该导出函数一样的参数列表,并且我自己的函数必须去调用该导出函数
导出函数原型:int DLLInterface(char *, const char *, ...);
我的函数原型:int MyDLLInterface(char *, const char *, ...);
要求我的MyDLLInterface必须去调用DLLInterface
int MyDLLInterface(char *, const char *, ...)
{
    // do something
    DLLInterface(,,...); // 关键就是这里,我必须调用DLLInterface,参数如何传入?
    // do something
    return 0;
}
请大家抛开sprintf这个函数,我很后悔提到这个函数
谢谢!

仔细看#12代码,不都实现了么
就算编译器不支持变长模板函数,那也可以写N个模板函数重载,也同样完成目标。

#18


int MyDLLInterface(char *, const char *, ...)
问题是:
第一个 参数 要 指出 有 几个 参数。 否则:
va_arg, va_end, va_start
没法 终止。

引用:
例如 int max(int n, ...); 其函数内部应该如此实现:
#include <iostream.h> 
void fun(int a, ...) 
// 这个 a 是 实际参数 个数
  int *temp = &a;
  temp++;
  for (int i = 0; i < a; ++i) 
  { 
    cout << *temp << endl; 
    temp++; 
  } 
}
int main() 

  int a = 1; 
  int b = 2; 
  int c = 3; 
  int d = 4; 
  fun( 4, a, b, c, d); 
  system("pause"); 
  return 0; 
}

#19


按#12给的不能满足楼主的要求的话,楼主在详细描述下功能需求吧


#include <iostream>

int DLLInterface(char * output, const char * input, ...)
{
    std::cout << "ok" << std::endl;
    return(0);
}

#define MyDLLInterface(output, input, ...)        \
{                                                 \
    std::cout << "in ..." << std::endl;           \
    DLLInterface(output, input, ##__VA_ARGS__);   \
    std::cout << "out ..." << std::endl;          \
}

int main(int argc, char * argv[])
{
    char output[1];
    const char * input = "%s %d";
    MyDLLInterface(output, input, "test", 1);
    return(0);
}


用宏来做的话,VS2010,GCC编译器都是支持的

#20


上面宏的缺陷,没有返回值,
这个可以通过给MyDLLInterface多加一个“输出参数”来完成:

#define MyDLLInterface(ret, output, input, ...)         \
do                                                      \
{                                                       \
    std::cout << "in ..." << std::endl;                 \
    ret = DLLInterface(output, input, ##__VA_ARGS__);   \
    std::cout << "out ..." << std::endl;                \
} while(false)

#21


该回复于2014-03-15 09:30:33被管理员删除

#22


感谢大家的答复,尤其是akirya的好心相助!
我在VC2010上测试发现它不支持变长模板参数,现在暂用函数重载的方法,好在现在的调用形式不多,只有5、6种。
另外再请教akirya,这个DLL里还有一个导出函数,它的省略号部分可以进行参数带回,请问,这样的函数C++是否也有什么新特性来支持?谢谢!

#23


引用 22 楼 ylyan20xx 的回复:
感谢大家的答复,尤其是akirya的好心相助!
我在VC2010上测试发现它不支持变长模板参数,现在暂用函数重载的方法,好在现在的调用形式不多,只有5、6种。
另外再请教akirya,这个DLL里还有一个导出函数,它的省略号部分可以进行参数带回,请问,这样的函数C++是否也有什么新特性来支持?谢谢!

仔细看#12代码,函数模板上已经写了。

#24


楼主,我直接封装了这个方法,用作日志记录。一会儿给你代码

#1


应该用 vsprintf

#2


感谢#1朋友,谢谢!
事实上,我在描述问题时进行了类比简化。我的实际问题跟sprintf相似,于是我就用它做了类比。我的实际问题是,第三方提供的DLL的接口函数的参数中含有省略号,而我要对其封装调用。这个DLL属于工控方面的,函数实现向PCI总线发送一些指令,所以显然没法用vsprintf

#3


所以,这个问题的一般性描述就是:如何对含有省略号参数的函数进行封装?(封装它,而不是寻找它的替代物)

#4


引用 3 楼 ylyan20xx 的回复:
所以,这个问题的一般性描述就是:如何对含有省略号参数的函数进行封装?(封装它,而不是寻找它的替代物)

如果编译器支持的话,用变长函数模板
不行的话,就用宏吧。其他方法都有限制,不好搞。

#5


直接封装sprintf必须自己解释sFormat参数中的参数个数和类型,否则无法提取参数,然后估计得用汇编重建sprintf的参数表,然后再用汇编调用sprintf

#6


msdn:
va_arg, va_end, va_start
//
int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /* Initialize variable arguments. */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}

#7


引用 6 楼 schlafenhamster 的回复:
msdn:
va_arg, va_end, va_start
//
int average( int first, ... )
{
   int count = 0, sum = 0, i = first;
   va_list marker;

   va_start( marker, first );     /* Initialize variable arguments. */
   while( i != -1 )
   {
      sum += i;
      count++;
      i = va_arg( marker, int);
   }
   va_end( marker );              /* Reset variable arguments.      */
   return( sum ? (sum / count) : 0 );
}


使用特定值(-1)作为可变参数参数表结束,在很多场合不适用,特别是参数类型不一致的时候更是如此

#8


...不定参数不能多级传递. 只有特殊的函数才接收这个外部传递的...参数.

给你个参考:
//注意, ... 可变参数不能传再次传递, 只有v开头的几个特殊函数可以处理.
void _TraceT(LPCTSTR lpszFmt, ...)
{
va_list args;
va_start(args, lpszFmt);
int len = _vsctprintf(lpszFmt, args)+1;
TCHAR *lpszBuf = (TCHAR*)_alloca(len*sizeof(TCHAR));//栈中分配, 不需要释放
_vstprintf_s(lpszBuf, len, lpszFmt, args);
va_end(args);
OutputDebugString(lpszBuf);


}

#9


" int first"

可以代入: "%d%d%d..."

#10


感谢以上好心人!
average的例子我也看过,但它不比sprintf,再者,我的问题是对含有省略号参数的函数进行直接封装,而不是寻找它的替代物
回楼上:你讲到“不定参数不能多级传递”,那是否意味着我的问题(对含有省略号参数的函数进行直接封装)根本就不成立?

#11


vsprintf 其实内部是能取得第一个变量的地址,根据地址来取值的。如果你封装 sprintf 则你也得想办法把地址传给它,但实际上 sprintf 只接受值,而不能传递 "..." 处第一个参数的地址进去。

#12


引用 10 楼 ylyan20xx 的回复:
感谢以上好心人!
average的例子我也看过,但它不比sprintf,再者,我的问题是对含有省略号参数的函数进行直接封装,而不是寻找它的替代物
回楼上:你讲到“不定参数不能多级传递”,那是否意味着我的问题(对含有省略号参数的函数进行直接封装)根本就不成立?

//变长模板参数,需要C++11支持
template<class ... T>
int func( T... v )
{
puts("------");
int r = printf( v... );
puts("======");
return r;
}

//变长参数宏,不一定所有编译器都支持
#define FUNC( ... )  \
{ \
puts("macro ------"); \
printf( __VA_ARGS__ ); \
puts("macro ======"); \
}


int main()
{
func( "%d,%f,%c\n" , 1,2.0 , 'c' );
FUNC( "%d,%f,%c\n" , 1,2.0 , 'c' );
    return 0;
}

其他方案都有缺陷...

#13


引用 12 楼 akirya 的回复:

//变长模板参数,需要C++11支持
template<class ... T>
int func( T... v )
{
puts("------");
int r = printf( v... );
puts("======");
return r;
}

//变长参数宏,不一定所有编译器都支持
#define FUNC( ... )  \
{ \
puts("macro ------"); \
printf( __VA_ARGS__ ); \
puts("macro ======"); \
}


int main()
{
func( "%d,%f,%c\n" , 1,2.0 , 'c' );
FUNC( "%d,%f,%c\n" , 1,2.0 , 'c' );
    return 0;
}

赞一个  请教大牛!如何封装sprintf?很急!!

#14


非常感谢大家!
回#12大牛:发帖时为了描述更具体,我用了sprintf,而我实际要封装的函数跟sprintf函数在功能上是完全不同的,它们仅仅是形式一样,即:都是参数列表中带有省略号。我要封装的是第三方提供的DLL里的一个导出函数。不知有没更一般性的方法?谢谢!

#15


如果楼主你细心, 已经发现有三种方案了. 

还管你是想要自己格式化串, 还是要遍历参数, 都已经给也方案了.
其它的, 还有什么问题吧.

或者你可以再详细的描述下你的问题, 最好是举几个例子, 你想做成什么效果.

#16


回楼上大牛:小弟功力浅显,不能确定你在#12贴的代码是否具有一般性
都怪我提到了sprintf,害了大家去往这个地方思考
我重新描述我的问题吧:
对第三方DLL的一个导出函数(其参数列表中带有省略号)进行封装,要求自己的函数具有与该导出函数一样的参数列表,并且我自己的函数必须去调用该导出函数
导出函数原型:int DLLInterface(char *, const char *, ...);
我的函数原型:int MyDLLInterface(char *, const char *, ...);
要求我的MyDLLInterface必须去调用DLLInterface
int MyDLLInterface(char *, const char *, ...)
{
    // do something
    DLLInterface(,,...); // 关键就是这里,我必须调用DLLInterface,参数如何传入?
    // do something
    return 0;
}
请大家抛开sprintf这个函数,我很后悔提到这个函数
谢谢!

#17


引用 16 楼 ylyan20xx 的回复:
回楼上大牛:小弟功力浅显,不能确定你在#12贴的代码是否具有一般性
都怪我提到了sprintf,害了大家去往这个地方思考
我重新描述我的问题吧:
对第三方DLL的一个导出函数(其参数列表中带有省略号)进行封装,要求自己的函数具有与该导出函数一样的参数列表,并且我自己的函数必须去调用该导出函数
导出函数原型:int DLLInterface(char *, const char *, ...);
我的函数原型:int MyDLLInterface(char *, const char *, ...);
要求我的MyDLLInterface必须去调用DLLInterface
int MyDLLInterface(char *, const char *, ...)
{
    // do something
    DLLInterface(,,...); // 关键就是这里,我必须调用DLLInterface,参数如何传入?
    // do something
    return 0;
}
请大家抛开sprintf这个函数,我很后悔提到这个函数
谢谢!

仔细看#12代码,不都实现了么
就算编译器不支持变长模板函数,那也可以写N个模板函数重载,也同样完成目标。

#18


int MyDLLInterface(char *, const char *, ...)
问题是:
第一个 参数 要 指出 有 几个 参数。 否则:
va_arg, va_end, va_start
没法 终止。

引用:
例如 int max(int n, ...); 其函数内部应该如此实现:
#include <iostream.h> 
void fun(int a, ...) 
// 这个 a 是 实际参数 个数
  int *temp = &a;
  temp++;
  for (int i = 0; i < a; ++i) 
  { 
    cout << *temp << endl; 
    temp++; 
  } 
}
int main() 

  int a = 1; 
  int b = 2; 
  int c = 3; 
  int d = 4; 
  fun( 4, a, b, c, d); 
  system("pause"); 
  return 0; 
}

#19


按#12给的不能满足楼主的要求的话,楼主在详细描述下功能需求吧


#include <iostream>

int DLLInterface(char * output, const char * input, ...)
{
    std::cout << "ok" << std::endl;
    return(0);
}

#define MyDLLInterface(output, input, ...)        \
{                                                 \
    std::cout << "in ..." << std::endl;           \
    DLLInterface(output, input, ##__VA_ARGS__);   \
    std::cout << "out ..." << std::endl;          \
}

int main(int argc, char * argv[])
{
    char output[1];
    const char * input = "%s %d";
    MyDLLInterface(output, input, "test", 1);
    return(0);
}


用宏来做的话,VS2010,GCC编译器都是支持的

#20


上面宏的缺陷,没有返回值,
这个可以通过给MyDLLInterface多加一个“输出参数”来完成:

#define MyDLLInterface(ret, output, input, ...)         \
do                                                      \
{                                                       \
    std::cout << "in ..." << std::endl;                 \
    ret = DLLInterface(output, input, ##__VA_ARGS__);   \
    std::cout << "out ..." << std::endl;                \
} while(false)

#21


该回复于2014-03-15 09:30:33被管理员删除

#22


感谢大家的答复,尤其是akirya的好心相助!
我在VC2010上测试发现它不支持变长模板参数,现在暂用函数重载的方法,好在现在的调用形式不多,只有5、6种。
另外再请教akirya,这个DLL里还有一个导出函数,它的省略号部分可以进行参数带回,请问,这样的函数C++是否也有什么新特性来支持?谢谢!

#23


引用 22 楼 ylyan20xx 的回复:
感谢大家的答复,尤其是akirya的好心相助!
我在VC2010上测试发现它不支持变长模板参数,现在暂用函数重载的方法,好在现在的调用形式不多,只有5、6种。
另外再请教akirya,这个DLL里还有一个导出函数,它的省略号部分可以进行参数带回,请问,这样的函数C++是否也有什么新特性来支持?谢谢!

仔细看#12代码,函数模板上已经写了。

#24


楼主,我直接封装了这个方法,用作日志记录。一会儿给你代码