在 c++ header-only 库中定义 "hidden" 常量
Defining "hidden" constants in c++ header-only libraries
我正在创建库的 C++ header-only 实现,我在其中定义了多个我只想在库中使用的常量(定义为 static constexpr
s),以生成我的代码更清晰易读。我认为拥有内部链接就足以从我包含该库的任何其他源文件中“隐藏”这些常量。
然而,在我为它编写单元测试的另一个文件中包含这个 header 之后,我得到了像 redefinition of '<constant_name>'
这样的错误(测试源文件有自己的常量名称,因为它们在某些测试用例中很有用,也定义为 static constexpr
s)。经过进一步阅读,我现在明白内部链接意味着常量在翻译单元之外不可用,但翻译单元包括源文件 以及 任何 header它包括。这可以解释为什么我会收到 redefinition
错误,因为测试源文件和库 header 文件中都存在同名常量,并且它们最终成为一个翻译单元。
目前,我已将所述常量包装在命名空间中以避免冲突。然而,这并不理想。例如,当我使用命名空间作为前缀时,我仍然会在其他文件中获得这些常量的自动完成建议。相反,我希望它们完全隐藏。
我的问题是关于是否有任何解决方法。有没有办法让这些常量只在 header 本身内真正可见,而对包含它的任何文件都不可见?
编辑:这里有一些代码可以证明我的意思。
library.hpp
static constexpr int USEFUL_CONSTANT = 5;
namespace library {
...library implementation here...
}
tests.cpp
#include "library.hpp"
// Constant happens to be useful in tests too, but I don't
// want to expose it for everybody, so I redefine it here
static constexpr int USEFUL_CONSTANT = 5;
...tests here...
我收到重定义错误,因为两个常量都属于同一个翻译单元。
C++ header 文件只是通过 pre-processor.copy-pasted 将源代码“copy-pasted”。
因此,从包含 header 的地方隐藏它们的一种方法是对这些常量使用 pre-processor #define
。请注意,在现代 C++ 中使用 #define
被认为是一种不好的做法,只有当它真正使代码更清晰和更易于维护时才应使用它。有这种情况吗?这是相当主观的,但无论好坏,这仍然是实现你想要的东西的一种方法,而且简单易懂。
mylib.h
:
#ifndef MYLIB_H
#defein MYLIB_H
// local constants of library, undefined at end
#define MYLIB_CUSTOM_PI (3.2)
// end of local constant defines
//... rest of the code
// undefine local constants
#undef MYLIB_CUSTOM_PI
#endif // MYLIB_H
这具有使用 pre-processor 宏的缺点,但由于您仅在本地使用它们而不将它们公开给库的用户,因此它应该可以正常工作。这仅适用于原始类型(包括 "string literal"
即 const char*
并且编译器将忽略它们)。例如STL容器值不应该用宏定义(因为在每个使用宏的地方都会构造一个新容器)。
几个可行的方法:
- 内部命名空间,(boost-style)
namespace mylib
{
namespace detail
{
inline constexpr int const goodenough{42};
}
int foo(void)
{
return detail::goodenough;
}
}
- 转换为某些 class 的
private
static
字段,并通过 friend
声明 授予访问权限
namespace mylib
{
int foo(void);
class detail
{
friend int ::mylib::foo(void);
static inline constexpr int const goodenough{42};
};
int foo(void)
{
return detail::goodenough;
}
}
注意:constexpr
意味着 inline
和 const
。
我正在创建库的 C++ header-only 实现,我在其中定义了多个我只想在库中使用的常量(定义为 static constexpr
s),以生成我的代码更清晰易读。我认为拥有内部链接就足以从我包含该库的任何其他源文件中“隐藏”这些常量。
然而,在我为它编写单元测试的另一个文件中包含这个 header 之后,我得到了像 redefinition of '<constant_name>'
这样的错误(测试源文件有自己的常量名称,因为它们在某些测试用例中很有用,也定义为 static constexpr
s)。经过进一步阅读,我现在明白内部链接意味着常量在翻译单元之外不可用,但翻译单元包括源文件 以及 任何 header它包括。这可以解释为什么我会收到 redefinition
错误,因为测试源文件和库 header 文件中都存在同名常量,并且它们最终成为一个翻译单元。
目前,我已将所述常量包装在命名空间中以避免冲突。然而,这并不理想。例如,当我使用命名空间作为前缀时,我仍然会在其他文件中获得这些常量的自动完成建议。相反,我希望它们完全隐藏。
我的问题是关于是否有任何解决方法。有没有办法让这些常量只在 header 本身内真正可见,而对包含它的任何文件都不可见?
编辑:这里有一些代码可以证明我的意思。
library.hpp
static constexpr int USEFUL_CONSTANT = 5;
namespace library {
...library implementation here...
}
tests.cpp
#include "library.hpp"
// Constant happens to be useful in tests too, but I don't
// want to expose it for everybody, so I redefine it here
static constexpr int USEFUL_CONSTANT = 5;
...tests here...
我收到重定义错误,因为两个常量都属于同一个翻译单元。
C++ header 文件只是通过 pre-processor.copy-pasted 将源代码“copy-pasted”。
因此,从包含 header 的地方隐藏它们的一种方法是对这些常量使用 pre-processor #define
。请注意,在现代 C++ 中使用 #define
被认为是一种不好的做法,只有当它真正使代码更清晰和更易于维护时才应使用它。有这种情况吗?这是相当主观的,但无论好坏,这仍然是实现你想要的东西的一种方法,而且简单易懂。
mylib.h
:
#ifndef MYLIB_H
#defein MYLIB_H
// local constants of library, undefined at end
#define MYLIB_CUSTOM_PI (3.2)
// end of local constant defines
//... rest of the code
// undefine local constants
#undef MYLIB_CUSTOM_PI
#endif // MYLIB_H
这具有使用 pre-processor 宏的缺点,但由于您仅在本地使用它们而不将它们公开给库的用户,因此它应该可以正常工作。这仅适用于原始类型(包括 "string literal"
即 const char*
并且编译器将忽略它们)。例如STL容器值不应该用宏定义(因为在每个使用宏的地方都会构造一个新容器)。
几个可行的方法:
- 内部命名空间,(boost-style)
namespace mylib
{
namespace detail
{
inline constexpr int const goodenough{42};
}
int foo(void)
{
return detail::goodenough;
}
}
- 转换为某些 class 的
private
static
字段,并通过friend
声明 授予访问权限
namespace mylib
{
int foo(void);
class detail
{
friend int ::mylib::foo(void);
static inline constexpr int const goodenough{42};
};
int foo(void)
{
return detail::goodenough;
}
}
注意:constexpr
意味着 inline
和 const
。