在C++中获取命名空间名称的任何可移植技巧
Any portable tricks to obtain namespace name in C++?
我有一些格式良好的代码,如下所示:
NAMESPACE_BEGIN(Foo)
inline void test() {
string s = xxx;
}
NAMESPACE_END(Foo)
那么,使用NAMESPACE_BEGIN()
宏获取命名空间名称"Foo"是否有任何可移植技巧test()
?
我正在考虑这样的事情,但它肯定会导致符号重新定义:
#define NAMESPACE_BEGIN(x)
namespace x {
inline const char *_curNamespace() {
return #x;
}
#define NAMESPACE_END(x)
}
还有一个解决方法看起来像这样,但这不是很方便
#define NAMESPACE_NAME Foo
// using a header file so that we can use #ifdef guard
#include "MyNamespaceBegin.h"
...
#include "MyNamespaceEnd.h"
编辑:
为什么我需要这个:
我正在使用大部分宏来生成代码以实现一些 动态反射逻辑(是的,不是静态模板反射), 通过使用静态成员函数在类范围内没问题, 但不适用于命名空间
为什么不手动声明一次名称 getter:
我想要的是这样的东西:
// the global default version const char *_curNamespace() {return "";} namespace X { // the local namespace version const char *_curNamespace() {return "X";} // some verbose reflection register code ... registerSomething(_curNamespace()); ... }
当然,所有详细的寄存器代码都应该由宏生成
而且,应用级用户不应该关心
_curNamespace()
, 所以,我想简化用户的使用,在任何情况下都使用自定义NAMESPACE_BEGIN(xxx)
宏如果你仍然对我在做什么感到好奇, 检查这个: https://github.com/ZFFramework/ZFFramework
我使用了很多技巧来实现纯C++的完全动态反射, 为了实现我的一些奇思妙想, 目前,这个项目只是为了好玩, 我不知道它是否具有实用性
编辑2:
目前,我认为最好的解决方法应该是这样的:
#define NAMESPACE_BEGIN(ns)
namespace ns {
extern const char *__curNS();
#define NAMESPACE_END(ns)
}
#define NAMESPACE_REG(ns)
const char *__curNS() {return #ns;}
- 应用级用户仍然只需要关心
NAMESPACE_BEGIN
NAMESPACE_REG
必须在源文件中只声明一次- 否则,将发生未定义的符号
- 如果多次,将发生重复的符号
- 虽然这很烦人,有时你需要额外的源文件 要握住
NAMESPACE_REG
, 严格的规则应该防止用户忘记丑陋的解决方法
你在实现起来微不足道的事情上大惊小怪。
首先,使用NAMESPACE_BEGIN
和NAMESPACE_END
对我来说似乎是不必要的。我不明白这比
namespace Foo
{
}
如果获取namespace
的名称很重要/有用,请添加一个简单的函数。
namespace Foo
{
inline std::string get_name() { return "Foo"; }
}
小型实际应用程序需要数千行代码。大型实际应用程序需要数百万行代码。从这个角度来看,实现单行inline
功能是一项非常小的任务。
此解决方案采用了一些预处理器魔法,并具有以下功能:
- 命名空间只被提及一次
- 访问包含不带引号的名称的宏
- 访问包含带引号的名称的宏
- 支持重复相同的命名空间
- 支持不同的命名空间
- 检测到开始/结束宏的误用
- 清理,即在开始/结束块之外没有定义额外的宏
它不支持嵌套命名空间。
用法示例:
#include "framework.hpp"
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
// Here you have access to NAMESPACE_NAME (unquoted, i.e. Foo)
// and also to NAMESPACE_NAME_STRING (quoted, i.e. "Foo")
#include NAMESPACE_END
// NAMESPACE_NAME and NAMESPACE_NAME_STRING do not exist
// outside the block, so they cannot be misused
// Different namespaces in the same TU are supported
#define NAMESPACE_NAME Bar
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
// Repeating the same namespace is also supported
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
实现如下:
framework.hpp
#pragma once
#define NAMESPACE_BEGIN "framework_namespace_begin.hpp"
#define NAMESPACE_END "framework_namespace_end.hpp"
framework_namespace_begin.hpp
#ifndef NAMESPACE_NAME
#error "NAMESPACE_NAME not defined"
#endif
#define NAMESPACE_IN_NAMESPACE 1
#define NAMESPACE_NAME_DO_STR(X) #X
#define NAMESPACE_NAME_STR(X) NAMESPACE_NAME_DO_STR(X)
#define NAMESPACE_NAME_STRING NAMESPACE_NAME_STR(NAMESPACE_NAME)
namespace NAMESPACE_NAME {
framework_namespace_end.hpp
#ifndef NAMESPACE_IN_NAMESPACE
#error "NAMESPACE_IN_NAMESPACE not defined"
#endif
}
#undef NAMESPACE_NAME
#undef NAMESPACE_NAME_STRING
#undef NAMESPACE_IN_NAMESPACE
你知道吗? 我想我可能有一个可行的解决方案。 它实际上非常简单,并且非常接近OP的原始建议(如果您想在同一翻译单元中打开命名空间两次,实际上只有潜在的重复定义问题)。 您只需要横向思考一下,不要太珍惜看到您的命名空间被宏而不是大括号括起来。
所以让我把它放在这里,因为它真的没有什么,然后我会解释为什么我个人碰巧喜欢它。
法典:
宏:
#define DECLARE_NAMESPACE(ns)
namespace ns {
static constexpr const char *_curNamespace = #ns;
}
#define BEGIN_NAMESPACE(ns)
namespace ns {
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
#define END_NAMESPACE }
示例代码:
#include <iostream>
DECLARE_NAMESPACE (Foo)
BEGIN_NAMESPACE (Foo)
void print_namespace_name () { std::cout << _curNamespace << "n"; }
END_NAMESPACE
BEGIN_NAMESPACE (Foo)
void another_print_namespace_name () { std::cout << _curNamespace << "n"; }
END_NAMESPACE
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
void print_namespace_name () { std::cout << _curNamespace << "n"; }
DECLARE_NAMESPACE (BarBar)
BEGIN_NAMESPACE (BarBar)
void print_namespace_name () { std::cout << _curNamespace << "n"; }
END_NAMESPACE
END_NAMESPACE
int main ()
{
Foo::print_namespace_name ();
Foo::another_print_namespace_name ();
Bar::print_namespace_name ();
Bar::BarBar::print_namespace_name ();
}
输出:
Foo
Foo
Bar
BarBar
现在,这显然非常容易实现,也易于使用,并且没有明显的限制。 特别是,它可以处理嵌套命名空间(如上面的代码所示),并且在同一编译单元中打开命名空间两次也可以工作(同样,这显示在代码片段中)。
但是,但是,但是,我们是否仍然需要两次输入命名空间的名称,这难道不是我们试图避免消除拼写错误的事情吗?
好吧,当然,我们必须输入两次名称,但那又怎样,忍受它。 关键是,有了这组特定的宏,编译器现在将为我们捕获任何拼写错误。 让我们通过故意放一个来证明这一点。 所以这个:
DECLARE_NAMESPACE Whoops
BEGIN_NAMESPACE whoops
END_NAMESPACE
生成这个(我找不到更好的方法来制定static_assert
,抱歉):
prog.cc:12:24: error: '_curNamespace' is not a member of 'whoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:27:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (whoops)
^~~~~~~~~~~~~~~
更重要的是(这就是为什么我们需要BEGIN_NAMESPACE
宏):
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
DECLARE_NAMESPACE (BarWhoops)
BEGIN_NAMESPACE (Barwhoops)
END_NAMESPACE
END_NAMESPACE
生成此内容:
prog.cc:12:24: error: '_curNamespace' is not a member of 'Bar::Barwhoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:42:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (Barwhoops)
^~~~~~~~~~~~~~~
这只是花花公子。
所以,你知道,有什么不喜欢的?
现场演示 - 取消注释第 3 行以查看这些编译器错误。
您可以使用变量并使用"NAMESPACE_BEGIN"和"NAMESPACE_END"更改其值
变量__name表示当前的完整命名空间位置
比如"abc::d ef::d etail">
std::string __name = "";
std::string & __append(std::string & str, const char * ptr) {
if (!str.empty()) {
str.append("::");
}
str.append(ptr);
return str;
}
std::string & __substr(std::string & str, const char * ptr) {
if (str.length() == strlen(ptr)) {
str.clear();
} else {
str = str.substr(0, str.length() - strlen(ptr) - 2);
}
return str;
}
#define NAMESPACE_NAME __name
#define CONCATENATE_DIRECT(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
#ifdef _MSC_VER
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __COUNTER__)
#else
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#endif
#define APPEND_NAME(x) std::string ANONYMOUS_VARIABLE(__start_name) = __append(__name, #x)
#define SUBSTR_NAME(x) std::string ANONYMOUS_VARIABLE(__end_name) = __substr(__name, #x)
#define NAMESPACE_BEGIN(x)
namespace x {
APPEND_NAME(x);
#define NAMESPACE_END(x)
SUBSTR_NAME(x);
}
然后,您可以使用NAMESPACE_NAME宏作为全名,也可以从中提取姓氏
这里有一种方法。 核心思想来自思路:
问:如何定义可从同一范围访问的具有相同名称的多个事物?
答:使它们都具有不同的参数类型。如果它们都有相同的身体,那么被称为哪一个并不重要。
问:如何生成一组无限的不同参数类型?
答:类模板。
问:如何确保对该重载函数集的调用永远不会模棱两可?
答:确保二元关系"隐式可转换自"是参数类型的完整排序,并为参数类型使用唯一的最小元素。
#include <type_traits>
#include <functional>
struct NamespaceHandleObj {
template <const NamespaceHandleObj* Handle1, const NamespaceHandleObj* Handle2>
struct less : public std::bool_constant<std::less<>{}(Handle1, Handle2)> {};
};
template <>
struct NamespaceHandleObj::less<nullptr, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle1>
struct NamespaceHandleObj::less<Handle1, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle2>
struct NamespaceHandleObj::less<nullptr, Handle2> : public std::true_type {};
template <const NamespaceHandleObj* Handle>
struct NamespaceParamType
{
constexpr NamespaceParamType() noexcept = default;
template <const NamespaceHandleObj* Other,
typename = std::enable_if_t<NamespaceHandleObj::less<Other, Handle>::value>>
constexpr NamespaceParamType(NamespaceParamType<Other>) noexcept {}
};
#define NAMESPACE_UTILS_TOSTR1(x) #x
#define NAMESPACE_UTILS_TOSTR(x) NAMESPACE_UTILS_TOSTR1(x)
#define BEGIN_NAMESPACE(ns)
namespace ns {
namespace {
constexpr NamespaceHandleObj namespace_handle_{};
constexpr const char* current_ns_(
NamespaceParamType<&namespace_handle_>) noexcept
{ return NAMESPACE_UTILS_TOSTR(ns); }
}
#define END_NAMESPACE }
#define CURRENT_NAMESPACE (current_ns_(NamespaceParamType<nullptr>{}))
上面的代码是 C++17,但将其移植到以前的版本并不难,甚至一直移植到 C++03。
- C++Union/Struct位域的实现和可移植性
- 具有Qt事件循环的可移植通用共享库设置
- 没有执行策略的 std::transform_reduce 是可移植的吗?
- 如何在 c++ 中正确指定 #include 路径以使程序可移植
- 创建异构顶点数据数组的可移植方法
- FlatBuffers/Protobuf 中是否有支持任意 24 位有符号整数定义的可移植二进制序列化架构?
- 静态库可移植性
- 从非类型模板参数声明 constexpr 数组的可移植方法
- C++:Unicode 字符串文字的可移植性
- 如何使Visual Studio 2017 C++项目在计算机之间更具可移植性
- 尝试将 sfml 和 c++ 与 Windows 10 上的可移植 vscode 链接起来
- 在C++中获取命名空间名称的任何可移植技巧
- STR这个实现是否安全且可移植?
- 编写 std::copysign 的可移植 SSE/AVX 版本
- 是可移植的包装结构
- 将参数推送到调用堆栈 (C++) 的可移植方法
- 在为视频游戏实施基本的二进制序列化时,请担心可移植性
- 如何以可移植方式删除名称为 wchar_t 类型的文件C++
- 如何从 pjsip 发送电子邮件.是否有任何可用于发送电子邮件的默认方法
- 是否有任何Netbeans或Eclipse c++可移植与mingw包含