【问题标题】:Verifying variable arguments are of expected type验证变量参数是否为预期类型
【发布时间】:2011-09-22 06:02:12
【问题描述】:

我目前正在编写一个函数,该函数将采用可变数量的参数。我将参数的数量传递给函数,然后遍历参数列表。

每个传递的参数都应该是一个整数。我将把这个整数添加到一个整数向量中,稍后会用到。

我想确保某些小丑将来不会尝试向此函数传递除整数以外的其他东西。我认识到我可以检查 va_arg 中的当前参数以确保它不为 NULL,并且我可以使用类似 isanum(va_arg()) 的东西来确定它是否是一个有效的整数。我想我什至可以检查 sizeof(va_arg) 并将其与 sizeof(int) 进行比较并确保它们相等。

我是否可以运行任何其他检查来验证我是否已通过有效整数?

提前感谢您的帮助

【问题讨论】:

  • gcc 可以对printf 参数进行完整性检查,我很确定有一种方法可以将该行为扩展到自定义函数。显然,它极不便携,但如果你碰巧在使用 gcc,它可能值得研究。
  • 我正在使用 GCC -- 抱歉耽搁了。
  • 可变参数和类型安全不能混用。为了你的理智,请改为传递一个容器(你可以使用 boost::assign 来为调用者简化事情),或者一个迭代器范围,或者使用某种运算符链接接口(比如 iostreams 的工作方式)。

标签: c++ arguments variadic-functions


【解决方案1】:

没有明智的方法可以做到这一点。可变参数函数通过将参数的所有原始二进制表示连接成堆栈上的一大块数据来工作。所以它依赖于调用者和被调用者就参数的数量和类型达成一致(否则你最终会读到int,就好像它是float一样)。

至于你的具体想法:

  • va_arg() 是一个宏,它简单地将原始堆栈数据的一些字节解释为您指定的任何类型。因此,在其上调用 sizeof() 只会告诉您您要求的数据类型的大小。

  • 一般来说,不存在形成无效整数的原始二进制数据模式。所以假设的isanum() 行不通。

【讨论】:

  • 虽然感谢大家的反馈,但您直接回答了我的问题。谢谢。
【解决方案2】:

每个传递的参数都应该是一个整数。

如果你有 C++0x 编译器,我建议使用 initializer_list<int> 而不是可变参数:

#include <initializer_list>

void foo(std::initializer_list<int> numbers)
{
    my_vector.insert(my_vector.end(), numbers.begin(), numbers.end());
}

int main()
{
    foo( {2, 3, 5, 7} );
}

这是直截了当且完全类型安全的。

【讨论】:

  • 很遗憾,我这里的环境没有C++0x编译器,所以不能使用这个方法。不过很高兴知道,所以谢谢。
  • 你可以使用 boost::assign 得到接近这个的东西。
【解决方案3】:

每个传递的参数都应该是 一个整数。我将添加这个 整数到整数向量 以后会用到。

那为什么不直接接受整数向量呢?

void AddIntegers(const std::vector<int>& vec);

然后,您始终可以使用迭代器将向量连接在一起。

或者做一个这样的界面:

void AddInteger(int newInt);

甚至这个:

void AddIntegers(const int* integers, unsigned int numIntegers);

template <unsigned int Size>
void AddIntegers(int (&integers)[Size])
{
    AddIntegers(integers, Size);
}

int main()
{
    int i[] = {1, 2, 3, 4};
    AddIntegers(i);
}

如果您需要使用 C++03 编译器,这些将起作用。如果您有 C++0x 编译器,则可以使用更出色的解​​决方案。

【讨论】:

    【解决方案4】:

    变量参数在设计上是不安全的。您无法以任何方式检查用户是否传递了正确的类型。 C++0x 使用可变参数模板来拯救它,但现在没有多少编译器支持它(只有 GCC afaik)。

    【讨论】:

    • 如果所有参数都属于同一类型,则不需要可变参数模板。在这种情况下,initializer_list&lt;T&gt; 就足够了。
    【解决方案5】:

    不幸的是,真的没有办法做到这一点。 printf() 之类的函数很容易通过传递无效或错误数量的参数而被搞砸。

    在 C++ 中,这是一项高级功能,需要使用此类代码进行编程以确保传递正确的参数。

    【讨论】:

      【解决方案6】:

      您无法使用可变参数进行任何类型的类型检查。我建议改用迭代器范围(如标准库函数)或可能使用std::vector&lt;int&gt;。这样类型就不能被颠覆了。

      【讨论】:

        【解决方案7】:

        既然您使用的是 C++,那么如何重载一些运算符并一个接一个地传递参数?例如

        class MyFunction {
          std::vector<int> param;
          public:
          MyFunction() { /* some initialisation? */ }
          MyFunction &operator,(int eatMe) {
            param.push_back(eatMe);
            return *this;
          }
          ~MyFunction() {
             //the implementation of your function goes here
          }
        }
        

        那么你可以这样称呼它:

        MyFunction(),2,3,5,7;
        

        注意,逗号操作符的使用可能看起来很吓人,但在这种情况下它实际上很有帮助。它是可能的最低左结合运算符。

        如果你的函数需要一些额外的参数,不仅仅是int-s的未知长度,你可以在构造函数中传递它们。

        如果有人使用 int 以外的其他内容,则将使用默认逗号运算符(评估左侧、丢弃、评估右侧)。如果您不喜欢这样 - 选择不同的运营商,例如流式&lt;&lt; 或提升式%

        【讨论】:

        • 我通常为此使用operator(),并且还允许构造函数接受“first”参数,因此您可以编写例如我的函数(2)(3)(5)(7)。析构函数的技巧很简洁,我也用过,但有时使用显式调用更有用。
        【解决方案8】:

        如果您仅限于 C++03 并且您的所有参数都应该是整数,一个解决方案是简单地隐藏可变参数函数(例如在“详细”命名空间中)并为 1 创建一系列重载函数到 N 个参数。这些函数将是简单的内联函数,将调用转发到实际函数的可变参数版本。这样一来,您就拥有了一个真正的实现,没有运行时开销,并且您向调用者公开了一个类型安全的接口(如果调用者需要 N 个以上的参数,他总是可以使用 vararg 版本)。

        Boost.PP 还可以帮助生成这些类型的重复模式。

        当然,如果您对 C++0x 有一定程度的支持,那么问题可以通过多种方式解决,包括 initializer_list 或可变参数模板。

        【讨论】:

          【解决方案9】:

          为了说明我对 CygnusX1 答案的评论,您可以这样做:

          class MyFunction {
            std::vector<int> params;
            public:
            MyFunction() { (*this)(); }
            MyFunction(int eatMe) { (*this)(eatMe); }
            MyFunction& operator()(int eatMe) {
              params.push_back(eatMe);
              return *this;
            }
            void operator()() { 
              // use params to do something interesting
            }
          }
          
          MyFunction(2)(3)(5)(7)();
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多