2

我想强制函数参数只接受特定的定义。例如,#define OUTPUT 1考虑#define INPUT 0void restrictedFunction(int parameter);

我将如何强制restrictedFunction(int parameter)仅接受OUTPUTor INPUT

我还想考虑到另一个定义可能具有相同的值,例如#define LEFT 1#define RIGHT 0

因此,在这种情况下,我希望restrictedFunction(int parameter)能够仅OUTPUTINPUT具体地接受。

4

4 回答 4

3
typedef enum { INPUT = 0, OUTPUT = 1 } IO_Type;

void restrictedFunction(IO_Type parameter) { ... }

它并不绝对强制使用这些值(编译器会让某人编写restrictedFunction(4)),但它和你得到的一样好。

如果您真的想强制使用正确的类型,那么:

typedef enum { INPUT = 0, OUTPUT = 1 } IO_Type;
typedef struct { IO_Type io_type } IO_Param;

void restrictedFunction(IO_Param parameter) { ... }

在 C99 或更高版本中,您可以使用以下命令调用它:

restrictedFunction((IO_Param){ INPUT });

这是一个复合文字,动态创建一个结构。不完全清楚结构类型是否真的很买你,但它会迫使用户思考一下,并且当他们使用错误时可能会改善编译器的诊断(但他们可能restrictedFunction((IO_Param){ 4 });仍然可以使用)。

这意味着您的restrictedFunction()代码应该准备好验证参数:

void restrictedFunction(IO_Type io_type)
{
    switch (io_type)
    {
    case INPUT:
        ...do input handling...
        break;
    case OUTPUT:
        ...do output handling...
        break;
    default:
        assert(io_type != INPUT && io_type != OUTPUT);
        ...or other error handling...
        break;
    }
}
于 2012-10-12T19:29:09.037 回答
1

你可以使用枚举。

typedef enum TrafficDirection { INPUT = 0, OUTPUT = 1 } TrafficDirection;

restrictedFunction(TrafficDirection direction);

当然,这并不完美。只要您使用强制转换,您仍然可以将任何 int 传递给它。

restrictedFunction((TrafficDirection) 4);
于 2012-10-12T19:28:41.767 回答
1

您没有得到尽可能多的保护,但您可以:

enum func_type { INPUT, OUTPUT };
void restrictedFunction( enum func_type parameter );
于 2012-10-12T19:28:55.853 回答
1

您可以使用包装器来验证参数:

#define restrictedFunction(x) do {                          \
   static_assert((x) == INPUT || (x) == OUTPUT);            \
   assert(!strcmp(#x, "INPUT") || !strcmp(#x, "OUTPUT"));   \
   restrictedFunction(x);                                   \
} while(0)

笔记:

  • 这假设restrictedFunction()返回一个void.如果它返回一个您实际使用的值,您将需要类似 gcc 的复合语句http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html的东西。或者——更好——你可以使用BUILD_BUG_ON_ZERO(参见什么是“:-!!”在 C 代码中?),我一直忘记,因为它似乎不适用于 C++。
  • 就是“do ... while(0)吞下分号”;在这里并不重要。
  • static_assert()是编译时断言;有许多变体可用。这是一个链接,https://stackoverflow.com/a/9059896/318716,如果你没有自己的方便的话。
  • assert()是标准的运行时断言。
  • 使用 gcc 4.1.2 和我的版本,当两者替换为;时,static_assert(),您可以用assert()编译时断言替换运行时断言。请参见下面的示例。我没有用其他编译器测试过这个。!strcmp()==
  • x仅在宏扩展中使用一次,因为前四个引用仅在编译时使用。

当您实际定义函数时,您必须添加括号以禁用宏扩展,如:

void (restrictedFunction)(int x){ ... }

此外,如果您的代码有特殊情况(其代码没有?),您需要使用您需要编写restrictedFunction()的参数调用:foo,

  (restrictedFunction)(foo);

这是一个完整的示例,它对标准库函数进行了包装exit()

#include <stdlib.h>

#define CONCAT_TOKENS(a, b)     a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e)    enum{EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}
#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}

#define exit(x) do {                                                \
   ASSERTM((x) ==  EXIT_SUCCESS  || (x) ==  EXIT_FAILURE,  value);  \
   ASSERTM(#x  == "EXIT_SUCCESS" || #x  == "EXIT_FAILURE", symbol); \
   exit(x);                                                         \
} while(0)

int main(void) {
   exit(EXIT_SUCCESS); // good
   exit(EXIT_FAILURE); // good
   exit(0);  // bad
   exit(3);  // doubly bad
}

如果我尝试编译它,我会得到:

gcc foo.c -o foo
foo.c: In function 'main':
foo.c:17: error: enumerator value for 'symbol_ASSERT_line_17' is not an integer constant
foo.c:18: warning: division by zero
foo.c:18: error: enumerator value for 'value_ASSERT_line_18' is not an integer constant
foo.c:18: error: enumerator value for 'symbol_ASSERT_line_18' is not an integer constant
于 2012-10-12T20:12:53.913 回答