可变参数的参数能不能固定类型?然后如何计算实参的个数?

r_swordsman 2011-01-17 11:03:40
即一个函数
format(string s, object obj, ....)

第一个参数是 string 类型. 这个好说

1, 我想后面的都是 object 类型或他的子类型怎么办?
最好是类型不是 object或子类, 就编译不过. 实在不行在函数内部检查也可以,不是则抛出异常.
出于性能考虑和调用方便,可变参数最好限定指针类型, 即 object*, 实在不行也可以是 object&, 退一步值也可以 object.

2, 怎么知道可变参数的个数? 即 s 后面有多少个 object* 类型的参数?

使调用时可以这样:

string s;
object *a, *b, *c;

....

format(s, a);
format(s, a, b);
format(s, a, b, c);

// 以上都可以



另一个问题, 假设参数是值类型,


使调用时可以这样:

string s;
object a, b, c;

....

format(s, a);
format(s, a, b);
format(s, a, b, c);

// 以上都可以

// 那么也希望下面可以

format(s, 1);
format(s, 1, 2.3);
format(s, 1, 2.3, "abcdef");

// 即,怎么让 1 隐身转换成 object 的 子类 cinteger;
// 即,怎么让 2.3 隐身转换成 object 的 子类 cfloat;
// 即,怎么让 "abcdef" 隐身转换成 object 的 子类 string;

// 不能显示转换, 即不能 (cinteger)1, (cfloat)2.3






求指教.

...全文
223 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
ForestDB 2011-01-18
  • 打赏
  • 举报
回复
先把printf的原理读懂。
r_swordsman 2011-01-18
  • 打赏
  • 举报
回复
都没想法了?
prohibit 2011-01-17
  • 打赏
  • 举报
回复
参考一下printf函数的实现吧~~
feng4206yu 2011-01-17
  • 打赏
  • 举报
回复
楼主的问题有点奇怪.当我们调用printf("%d,%s",int,string);的时候是根据第一个参数中的内容(%d,%s什么的)来判断接下来的参数的类型的.如果是%d,则表示对应的参数是int类型.这样才能正确的移动参数指针到下一个参数的地址.如果用指针是再好不过,每次移动刚好4个字节.至于如何计算参数的个数,大可不必,第一个参数解析完了,表示整个函数已经结束了.
机智的呆呆 2011-01-17
  • 打赏
  • 举报
回复
机智的呆呆 2011-01-17
  • 打赏
  • 举报
回复
或者尝试c++0x中的Variadic Templates特性,最新gcc应该能支持吧(猜得。。)
Variadic Templates
Problems to be solved:
How to construct a class with 1, 2, 3, 4, 5, 6, 7, 8, 9, or ... initializers?
How to avoid constructing an object out of parts and then copying the result?
How to construct a tuple?
The last question is the key: Think tuple! If you can make and access general tuples the rest will follow.
Here is an example (from "A brief introduction to Variadic templates'' (see references)) implementing a general, type-safe, printf(). It would probably be better to use boost::format, but consider:


const string pi = "pi";
const char* m = "The value of %s is about %g (unless you live in %s).\n";
printf(m, pi, 3.14159, "Indiana");


The simplest case of printf() is when there are no arguments except the format string, so we'll handle that first:

void printf(const char* s)
{
while (s && *s) {
if (*s=='%' && *++s!='%') // make sure that there wasn't meant to be more arguments
// %% represents plain % in a format string
throw runtime_error("invalid format: missing arguments");
std::cout << *s++;
}
}


That done, we must handle printf() with more arguments:

template<typename T, typename... Args> // note the "..."
void printf(const char* s, T value, Args... args) // note the "..."
{
while (s && *s) {
if (*s=='%' && *++s!='%') { // a format specifier (ignore which one it is)
std::cout << value; // use first non-format argument
return printf(++s, args...); // "peel off" first argument
}
std::cout << *s++;
}
throw std::runtime error("extra arguments provided to printf");
}


This code simply ``peels off'' the first non-format argument and then calls itself recursively. When there are no more non-format arguments, it calls the first (simpler) printf() (above). This is rather standard functional programming done at compile time. Note how the overloading of << replaces the use of the (possibly erroneous) ``hint'' in the format specifier.
The Args...defines what is called a ``parameter pack.'' That's basically a sequence of (type/value) pairs from which you can ``peel off'' arguments starting with the first. When printf() is called with one argument, the first definition (printf(const char*)) is chosen. When printf() is called with two or more arguments, the second definition (printf(const char*, T value, Args... args)) is chosen, with the first argument as s, the second as value, and the rest (if any) bundled into the parameter pack args for later use. In the call


printf(++s, args...);

The parameter pack args is expanded so that the next argument can now be selected as value. This carries on until args is empty (so that the first printf() is called).
If you are familiar with functional programming, you should find this an unusual notation for a pretty standard technique. If not, here are some small technical examples that might help. First we can declare and use a simple variadic template function (just like printf() above):


template<class ... Types>
void f(Types ... args); // variadic template function
// (i.e. a function that can take an arbitrary number of arguments of arbitrary types)
f(); // OK: args contains no arguments
f(1); // OK: args contains one argument: int
f(2, 1.0); // OK: args contains two arguments: int and double

We can build a variadic type:

template<typename Head, typename... Tail>
class tuple<Head, Tail...>
: private tuple<Tail...> { // here is the recursion
// Basically, a tuple stores its head (first (type/value) pair
// and derives from the tuple of its tail (the rest ofthe (type/value) pairs.
// Note that the type is encoded in the type, not stored as data
typedef tuple<Tail...> inherited;
public:
tuple() { } // default: the empty tuple

// Construct tuple from separate arguments:
tuple(typename add_const_reference<Head>::type v, typename add_const_reference<Tail>::type... vtail)
: m_head(v), inherited(vtail...) { }

// Construct tuple from another tuple:
template<typename... VValues>
tuple(const tuple<VValues...>& other)
: m_head(other.head()), inherited(other.tail()) { }

template<typename... VValues>
tuple& operator=(const tuple<VValues...>& other) // assignment
{
m_head = other.head();
tail() = other.tail();
return *this;
}

typename add_reference<Head>::type head() { return m_head; }
typename add_reference<const Head>::type head() const { return m_head; }

inherited& tail() { return *this; }
const inherited& tail() const { return *this; }
protected:
Head m_head;
}

Given that definition, we can make tuples (and copy and manipulate them):

tuple<string,vector,double> tt("hello",{1,2,3,4},1.2);
string h = tt.head(); // "hello"
tuple<vector<int>,double> t2 = tt.tail(); // {{1,2,3,4},1.2};

It can get a bit tedious to mention all of those types, so often, we deduce them from argument types, e.g. using the standard library make_tuple():

template<class... Types>
tuple<Types...> make_tuple(Types&&... t) // this definition is somewhat simplified (see standard 20.5.2.2)
{
return tuple<Types...>(t);
}

string s = "Hello";
vector<int> v = {1,22,3,4,5};
auto x = make_tuple(s,v,1.2);


bdmh 2011-01-17
  • 打赏
  • 举报
回复
参数个数,你还是增加一个变量表示参数个数吧,就行main函数中那样

第二个问题,别钻牛角尖,你不转换,编译器不让你干,除非你换其他方式,比如模板
机智的呆呆 2011-01-17
  • 打赏
  • 举报
回复
通过第一个参数,其中的占位符来描述后面的参数个数以及相应的类型~~~
可以参考 printf %d %s %p,
%d对于一个参数,而且参数大小为sizeof(int)
同理%s对于一个参数。。。。
healer_kx 2011-01-17
  • 打赏
  • 举报
回复
可能实现不了。。。
feng4206yu 2011-01-17
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 r_swordsman 的回复:]
print("%d%s", 123) 这样有问题吗?
[/Quote]
应该是没有问题的.这里我不知道printf是如何处理第二个参数的.
我的猜测是因为参数是从右往左入栈,所以第一个入栈即最右边的参数其实和printf这个函数在栈内开辟的空间的地址是一样的,所以当解析第一个固定参数期间会判断当前参数的地址,如果这时参数的地址与printf这个函数在栈内开辟的空间的地址是一样的就表示已经解析完了,剩下的固定参数中未解析的直接赋0好了.当然这只是我的一点猜测.其实可以参考具体的实现.
r_swordsman 2011-01-17
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 feng4206yu 的回复:]
楼主的问题有点奇怪.当我们调用printf("%d,%s",int,string);的时候是根据第一个参数中的内容(%d,%s什么的)来判断接下来的参数的类型的.如果是%d,则表示对应的参数是int类型.这样才能正确的移动参数指针到下一个参数的地址.如果用指针是再好不过,每次移动刚好4个字节.至于如何计算参数的个数,大可不必,第一个参数解析完了,表示整个函数已经结束了.
[/Quote]

print("%d%s", 123) 这样有问题吗?

64,636

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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