我有一些格式正确的代码如下:
NAMESPACE_BEGIN(Foo)
inline void test() {
string s = xxx;
}
NAMESPACE_END(Foo)
那么,通过使用NAMESPACE_BEGIN()
宏在test()
中获得名称空间名称“ Foo”,是否有任何可移植的技巧?
我正在考虑这样的事情,但这肯定会导致符号重新定义:
#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 ++中的完全动态反射, 实现我的一些幻想 现在,这个项目只是为了好玩, 我不知道它是否具有实用性
EDIT2 :
就目前而言,我认为最好的解决方法应该是这样:
#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
,
严格的规则应防止用户忘记丑陋的解决方法答案 0 :(得分:1)
您正在为一些琐碎的实现大惊小怪。
首先,对我来说,NAMESPACE_BEGIN
和NAMESPACE_END
的使用似乎是不必要的。我看不出它比它更具可读性或有用性
namespace Foo
{
}
如果获取namespace
的名称很重要/很有用,请添加一个简单的函数。
namespace Foo
{
inline std::string get_name() { return "Foo"; }
}
小型现实应用程序需要数千行代码。大型现实应用程序需要数百万行代码。从这个角度来看,实现一行inline
函数是一项非常小的任务。
答案 1 :(得分:1)
此解决方案使用了一些预处理器魔术,并具有以下功能:
它不支持嵌套名称空间。
用法示例:
<asp:ListView ID="ListItems" class="block" SortExpression="DataType" ItemStyle-Wrap="false" runat="server">
<ItemTemplate >
<table>
<tr>
<asp:TextBox ID="Name" runat="server" class="inline" Text='<%# Eval("text") %>'></asp:TextBox>
</tr>
</table>
</ItemTemplate>
</asp:ListView>
实现如下:
#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
答案 2 :(得分:1)
你知道吗?我想我可能对此有一个可行的解决方案。它实际上非常简单,并且非常接近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)
^~~~~~~~~~~~~~~
那只是花花公子。
所以,你知道什么不喜欢?
Live demo-取消注释第3行以查看那些编译器错误。
答案 3 :(得分:0)
您可以使用变量,并使用“ NAMESPACE_BEGIN”和“ NAMESPACE_END”更改其值
变量__name代表当前的完整名称空间位置
例如“ abc :: def :: detail”
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宏作为全名,也可以从中提取姓氏
答案 4 :(得分:0)
这是一种方法。核心思想来自思路:
问:如何定义可从同一范围访问的具有相同名称的多个事物?
A:使它们的所有功能具有不同的参数类型。而且,如果他们所有的人都具有相同的身体,那叫谁都没关系。
问:我如何生成一组不同的参数类型?
A:类模板。
问:如何确保对那组重载函数的调用永远不会模棱两可?
A:确保二进制关系“可以从其隐式转换为”是对参数类型的完整排序,并为参数类型使用唯一的最小元素。
#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。