C 程序中带有默认参数的 C++ 头文件
C++ header file with default arguments in C program
我有一个 C++ 库,其函数在头文件中声明。我的函数声明包含默认参数。
我想通过 Wolfram Mathematica WSTP 模板编译器 (wscc) 在 Mathematica 中使用这个库。这需要为我的库编写一个 C 接口。这个我用过 pattern
#ifdef __cplusplus
extern "C" {
#endif
double my_function(double x, double abs_error = 1E-3);
#ifdef __cplusplus
}
#endif
防止在我的库中进行名称修改(用 C++ 编译)。但是默认参数呢?我不认为它们是标准 C。从 Wolfram Mathematica WSTP 模板编译器 (wscc),我发现
error: expected ‘;’, ‘,’ or ‘)’ before ‘=’ token
double abs_error = 1E-3,
我是否必须分别进行 C 和 C++ 声明(本质上是两个头文件)?这是一个常见问题还是与我使用 wscc 有关?也许 wscc 不支持这种语法,虽然它通常是可以接受的?
C does not support default arguments.
因此,我假设您想为您的 C++ 代码保留它们,但您可以要求 C 调用者(在您的例子中是 Mathematica)为所有参数传递值。
一种可能的方法是定义一个宏,它在 C++ 中扩展为默认值初始值设定项,但在 C 中什么也没有。它不漂亮,但它有效:
#ifdef __cplusplus
#define DEFAULT_VALUE(x) = x
#else
#define DEFAULT_VALUE(x)
#endif
#ifdef __cplusplus
extern "C" {
#endif
void foo(int x DEFAULT_VALUE(42), void *y DEFAULT_VALUE(nullptr));
// In C, this becomes void foo(int x, void *y);
// In C++, this becomes void foo(int x = 42, void *y = nullptr);
#ifdef __cplusplus
}
#endif
extern C 不仅仅可以停止名称修改。该函数具有 C 调用约定。它告诉 CPP 编译器 "you should now speak C"。这意味着异常不会传播,等等。而且 AFAIK C 没有默认值。
您可以在 CPP 文件中实现带有 C 调用约定的函数,这有时非常有用。您可以让 C 调用 CPU 代码,这很有用。
我怀疑编译器如何处理默认值取决于编译器编写者。如果这是真的,我至少可以想到几种方法,其中一种方法不涉及在调用 my_function
时将 abs_error
的值放在堆栈上。例如,编译器可能在堆栈上包含一个参数计数,函数本身使用该参数来发现 abs_error
尚未传递并设置默认值。然而,如果这样的编译器使用具有 C 调用约定但不报告错误的函数来执行此操作,那么它确实会非常不友好。我想我会测试它,只是为了确定。
与其使用宏黑客来解决 C 不支持默认参数的事实,不如引入一个间接层。
首先是您的 C++ 代码使用的特定于 C++ 的 header(我随意命名为 interface.h
。
double my_function_caller(double x, double abs_error = 1E-3);
和一个 C 特定的 header(我随意命名 the_c_header.h
)
double my_function(double x, double abs_error);
/* all other functions that have a C interface here */
实际上,人们可能希望在两个 header 中都包含守卫。
下一步是实际与 mathematica 接口的 C++ 编译单元(我随意命名 interface.cpp
)
#include "interface.h"
extern "C" // this is C++, so we don't need to test __cplusplus
{
#include "the_c_header.h"
}
double my_function_caller(double x, double error)
{
return my_function(x, error);
}
接下来就是如何调用函数的问题了。如果调用者是C++,那么它需要做的就是
#include "interface.h"
// and later in some code
double result = my_function_caller(x);
double another_result = my_function_caller(x, 1E-6);
如果调用者是 C 语言(使用 C 编译器构建),它只是
#include "the_c_header.h"
/* and later */
result = my_function(x, 1E-3);
another result = my_function(x, 1E-6);
与 macro-based 解决方案相比,这有明显的优点和缺点,包括;
- None 宏的传统缺点(不考虑作用域,不会与其他宏进行意外交互,运行 违反某些 C++ 开发指南,这些指南禁止将宏用于除包含保护之外的任何用途).
- 明确区分哪些代码是 C,哪些是 C++:只有
interface.cpp
需要注意同时拥有 #include "the_c_header.h"
和 #include "interface.h"
,并担心 C++ 的接口机制到 C。否则,C 编译单元(用 C 编译器编译)只需要 #include "the_c_header.h"
而 C++ 编译单元只需要 #include "interface.h"
.
interface.h
可以使用任何 C++ 语言特性(不仅仅是默认参数)。例如,如果您愿意,它声明的所有函数都可以放在名为 mathematica
的命名空间中。使用您的函数的 C++ 开发人员无需关心实际上隐藏在该调用中的 C 代码接口。
- 如果您将来决定 re-implement
my_function()
使用 mathematica 以外的其他东西,您可以。简单地放入 the_c_header.h
和 interface.cpp
的替换,然后重建。关注点分离意味着无需更改 interface.h
,所有 C++ 调用程序甚至不需要在增量构建中重新编译(当然,除非您出于其他原因更改 interface.h
) .
- 实际上,构建过程将检测两个 header 文件的错误使用。 C 编译器会在
interface.h
上阻塞,因为它使用 C++ 特定的功能。 C++ 编译器将在 extern "C"
上下文之外接受 the_c_header.h
的内容,但如果任何 C++ 代码直接调用 my_function()
(链接将需要 name-mangled 定义).
简而言之,这比宏方法需要更多的努力来设置,但在长期 运行.
中更容易维护
我有一个 C++ 库,其函数在头文件中声明。我的函数声明包含默认参数。
我想通过 Wolfram Mathematica WSTP 模板编译器 (wscc) 在 Mathematica 中使用这个库。这需要为我的库编写一个 C 接口。这个我用过 pattern
#ifdef __cplusplus
extern "C" {
#endif
double my_function(double x, double abs_error = 1E-3);
#ifdef __cplusplus
}
#endif
防止在我的库中进行名称修改(用 C++ 编译)。但是默认参数呢?我不认为它们是标准 C。从 Wolfram Mathematica WSTP 模板编译器 (wscc),我发现
error: expected ‘;’, ‘,’ or ‘)’ before ‘=’ token double abs_error = 1E-3,
我是否必须分别进行 C 和 C++ 声明(本质上是两个头文件)?这是一个常见问题还是与我使用 wscc 有关?也许 wscc 不支持这种语法,虽然它通常是可以接受的?
C does not support default arguments.
因此,我假设您想为您的 C++ 代码保留它们,但您可以要求 C 调用者(在您的例子中是 Mathematica)为所有参数传递值。
一种可能的方法是定义一个宏,它在 C++ 中扩展为默认值初始值设定项,但在 C 中什么也没有。它不漂亮,但它有效:
#ifdef __cplusplus
#define DEFAULT_VALUE(x) = x
#else
#define DEFAULT_VALUE(x)
#endif
#ifdef __cplusplus
extern "C" {
#endif
void foo(int x DEFAULT_VALUE(42), void *y DEFAULT_VALUE(nullptr));
// In C, this becomes void foo(int x, void *y);
// In C++, this becomes void foo(int x = 42, void *y = nullptr);
#ifdef __cplusplus
}
#endif
extern C 不仅仅可以停止名称修改。该函数具有 C 调用约定。它告诉 CPP 编译器 "you should now speak C"。这意味着异常不会传播,等等。而且 AFAIK C 没有默认值。
您可以在 CPP 文件中实现带有 C 调用约定的函数,这有时非常有用。您可以让 C 调用 CPU 代码,这很有用。
我怀疑编译器如何处理默认值取决于编译器编写者。如果这是真的,我至少可以想到几种方法,其中一种方法不涉及在调用 my_function
时将 abs_error
的值放在堆栈上。例如,编译器可能在堆栈上包含一个参数计数,函数本身使用该参数来发现 abs_error
尚未传递并设置默认值。然而,如果这样的编译器使用具有 C 调用约定但不报告错误的函数来执行此操作,那么它确实会非常不友好。我想我会测试它,只是为了确定。
与其使用宏黑客来解决 C 不支持默认参数的事实,不如引入一个间接层。
首先是您的 C++ 代码使用的特定于 C++ 的 header(我随意命名为 interface.h
。
double my_function_caller(double x, double abs_error = 1E-3);
和一个 C 特定的 header(我随意命名 the_c_header.h
)
double my_function(double x, double abs_error);
/* all other functions that have a C interface here */
实际上,人们可能希望在两个 header 中都包含守卫。
下一步是实际与 mathematica 接口的 C++ 编译单元(我随意命名 interface.cpp
)
#include "interface.h"
extern "C" // this is C++, so we don't need to test __cplusplus
{
#include "the_c_header.h"
}
double my_function_caller(double x, double error)
{
return my_function(x, error);
}
接下来就是如何调用函数的问题了。如果调用者是C++,那么它需要做的就是
#include "interface.h"
// and later in some code
double result = my_function_caller(x);
double another_result = my_function_caller(x, 1E-6);
如果调用者是 C 语言(使用 C 编译器构建),它只是
#include "the_c_header.h"
/* and later */
result = my_function(x, 1E-3);
another result = my_function(x, 1E-6);
与 macro-based 解决方案相比,这有明显的优点和缺点,包括;
- None 宏的传统缺点(不考虑作用域,不会与其他宏进行意外交互,运行 违反某些 C++ 开发指南,这些指南禁止将宏用于除包含保护之外的任何用途).
- 明确区分哪些代码是 C,哪些是 C++:只有
interface.cpp
需要注意同时拥有#include "the_c_header.h"
和#include "interface.h"
,并担心 C++ 的接口机制到 C。否则,C 编译单元(用 C 编译器编译)只需要#include "the_c_header.h"
而 C++ 编译单元只需要#include "interface.h"
. interface.h
可以使用任何 C++ 语言特性(不仅仅是默认参数)。例如,如果您愿意,它声明的所有函数都可以放在名为mathematica
的命名空间中。使用您的函数的 C++ 开发人员无需关心实际上隐藏在该调用中的 C 代码接口。- 如果您将来决定 re-implement
my_function()
使用 mathematica 以外的其他东西,您可以。简单地放入the_c_header.h
和interface.cpp
的替换,然后重建。关注点分离意味着无需更改interface.h
,所有 C++ 调用程序甚至不需要在增量构建中重新编译(当然,除非您出于其他原因更改interface.h
) . - 实际上,构建过程将检测两个 header 文件的错误使用。 C 编译器会在
interface.h
上阻塞,因为它使用 C++ 特定的功能。 C++ 编译器将在extern "C"
上下文之外接受the_c_header.h
的内容,但如果任何 C++ 代码直接调用my_function()
(链接将需要 name-mangled 定义).
简而言之,这比宏方法需要更多的努力来设置,但在长期 运行.
中更容易维护