C++ 基于标记分派的专用化,用于C+中的平台识别和仿真+;代码 背景

C++ 基于标记分派的专用化,用于C+中的平台识别和仿真+;代码 背景,c++,c++11,cross-platform,C++,C++11,Cross Platform,最近,我想在用C++11编写的框架中重构一些与平台相关的低级API。当前的API通过在编译时选择所有可能实现之一的条件包含来支持不同的平台。但是,在某些情况下,API应该支持更多选项,即模拟其他平台。然后我可以将当前API重定向到这些新添加的仿真API 由于平台集仍然是在编译时完全确定的,所以我可以使用模板专门化等技术来避免运行时开销。然而,潜在的代码库很大。为每个API(特别是自由函数)使用类模板专门化会使代码膨胀很多。函数模板(如果不使用包装器直接使用)很容易造成重载,也很难确定如何指定匹配

最近,我想在用C++11编写的框架中重构一些与平台相关的低级API。当前的API通过在编译时选择所有可能实现之一的条件包含来支持不同的平台。但是,在某些情况下,API应该支持更多选项,即模拟其他平台。然后我可以将当前API重定向到这些新添加的仿真API

由于平台集仍然是在编译时完全确定的,所以我可以使用模板专门化等技术来避免运行时开销。然而,潜在的代码库很大。为每个API(特别是自由函数)使用类模板专门化会使代码膨胀很多。函数模板(如果不使用包装器直接使用)很容易造成重载,也很难确定如何指定匹配顺序。因此,我认为使用标签调度可能更好

为了说明这一想法,这里有一个例子:

目前:

#ifdef _WIN32
#   define Platform_Win32 1
#endif

inline char
GetNativePathSeparator()
{
#if Platform_Win32
    rerturn '\\';
#else
    return '/';
#endif
}
今后:

#define Platform_Win32_ID 0x0001 // some hard-coded magic number here...

#ifdef _WIN32
#define NativePlatform Platform_Win32_ID
#endif

template<std::uintmax_t ID>
using PlatformID = std::integral_constant<std::uintmax_t, ID>;

// For "all" platforms, as the default fallback.
struct BaseTag
{};

struct Win32Tag : BaseTag, PlatformID<Platform_Win32_ID>
{};

struct NativePlatformTag : BaseTag, PlatformID<NativePlatform>
{};

// The new emluation APIs. These APIs should be available for all host platforms, even the actual implementation used depends on the emulated target platform.

inline char
GetPlatformPathSeparator(Win32Tag)
{
    rerturn '\\';
}

inline char
GetPlatformPathSeparator(BaseTag)
{
    rerturn '/';
}

// Hmm... the implementation is even "portable"!
inline char
GetNativePathSeparator()
{
    return GetPlatformPathSeparator(NativePlatformTag());
}

// Note there would be many APIs like this.
// Macros can be used here to simplify the code for forwarding calls and make it easy enough to maintain, but it is relative complex for template specialization approach.
还可以。。。但如果有什么不太自信的事情

struct GNUTag : POSIXTag // (perhaps not quite true)
{};

struct LinuxTag : GNUTag // (mainstream userland only?)
{};

struct AndroidTag : LinuxTag // (mostly true using C/C++?)
{};

struct BSDTag : POSIXTag // (somewhat questionable...)
{};

struct OSXTag : BSDTag // (ditto)
{};

// #ifdef __CYGWIN__ ??
// #if defined(_NEWLIB_VERSION) && defined(__SCLE) ??
甚至(见):

或者更糟:

//...
struct POSIX2013Tag : POSIXTag // XXX: POSIX2008Tag? What about depercation and removal of APIs?
{};

#if _POSIX_SOURCE >= 200809
using POSIXTag = POSIX2013Tag;
#else
//...
#endif
叹息。。。够了。。。(到处都有条件包含已被取消。)

我的问题 有任何现有技术吗

更具体地说,我对细节的以下方面感兴趣:

基本标记类的基本说明符中是否应该有
virtual

  • 如果是这样的话,主流实现(最近的G++/Clang++/MSVC++/trunk版本等)能否像简单的
    std::iterator_标记
    案例那样优化运行时开销

  • 如果没有,是否有其他解决方案可以全面(更)优雅地解决背景问题

请注意,虚拟基地方法有两个主要好处:

  • 明确保持基址不偶尔重复。这与像
    std::iterator_tag
    这样的简单情况明显不同,它们的层次结构设计在很早的时候是稳定的/几乎是固定的,以后不太可能(经常)扩展。(尽管有。)
  • 允许用户不知道模板参数的顺序,并准确了解此处介绍的所有不同标识符中表示的功能集/平台重叠。很自然,如果大多数用户没有使用这些特定于平台的接口,他们甚至不需要知道所有受支持平台的存在。它还简化了这些特定于平台的API的维护工作。这可以通过提供诸如Tabe+UNIONFooStand的辅助标签类型来实现,其中C++类型的系统一般不容易处理。
下面是第二点的一个更具体的“现实世界”示例:

struct Platform_Win32 /*: virtual Platform_WinNT */
{};

struct Platform_Win64 : /*virtual*/ Platform_Win32
{};

struct Platform_MinGW32 : /*virtual*/ Platform_Win32
{};

struct Platform_MinGW64 : /*virtual*/ Platform_MinGW32
{};

(此例已被简化。在这里,我不使用“代码> STD::TimealAlgFieldAudio//Case>将宏值和标签链接在一起,并假设所有Win32兼容的平台都基于WinNT,没有Win16/Win9x/WinCE,并且我还没有考虑我应该支持WinRT,甚至葡萄酒……

< P> <强>供参考:< /强>

您可能想看看GTK(或者您已经有了)是如何完成OS适配层的。这可能是一个很好的参考。信息技术 宣传在Unix/Linux、Windows和OS/X上工作

关于设计:

我在这里要做的是将问题分为API部分和实现部分。API将处理部分操作系统差异,而实现部分将处理其余部分。如何决定谁做什么? 这来自良好的API实践

例如,OS版本依赖关系将转到实现部分。 也可以组合一些非常相似的操作系统。如果你走极端, 您可以创建两个操作系统适配器:常规和windows:-)

关于虚拟成员:

通常,模板编程和虚拟成员被视为彼此的替代方案。你知道,快速准确的代码,自动类型检查和多态性的能力


在这里,我看不出在同一个解决方案中包含这两种模式有什么好处。它使人困惑。当然,这只是我的看法。

成员可以多重继承。只要找到的所有声明相同,在多个基中找到的成员名称仍然可用。确实需要一个显式的作用域操作符来进行名称查找;此解决方案不能纯粹作为隐式转换

struct foo_bar_tag {
    typedef foo_bar_tag foo_tag;
};

struct a_tag : foo_bar_tag {};
struct b_tag : foo_bar_tag {};
struct d_tag : a_tag, b_tag {};

template< typename has_foo_tag >
typename has_foo_tag::foo_tag
slice_foo_tag( has_foo_tag ) { return {}; }

void f( foo_bar_tag );
void f( foo_baz_tag );

template< typename full_tag >
void g( full_tag t ) {
    f( t ); // error, ambiguous base conversion
    f( slice_foo_tag( t ) ); // OK
    f( typename full_tag::foo_tag{} ); // OK
}
struct foo\u bar\u标记{
类型def foo_bar_tag foo_tag;
};
结构a_标记:foo_bar_标记{};
结构b_标记:foo_bar_标记{};
结构d_标记:a_标记,b_标记{};
模板
typename有\u foo\u标记::foo\u标记
slice_foo_标记(has_foo_标记){return{};}
无效f(foo_bar_标签);
无效f(foo_baz_标签);
模板
无效g(完整标签t){
f(t);//错误,基转换不明确
f(slice_foo_tag(t));//确定
f(typename full_tag::foo_tag{});//确定
}
如果标记基类型是模板化的,则可以免费获取成员typedef(作为注入的类名)

template< typename >
struct foo_tag {};

struct a_tag : foo_tag< bar > {};
struct b_tag : foo_tag< bar > {};
struct d_tag : a_tag, b_tag {};

void f( foo_tag< bar > );
void f( foo_tag< baz > );

// Rest of example is the same.
// foo_tag< bar >::foo_tag is a typedef to foo_tag< bar >.
模板
结构foo_标记{};
结构a_标签:foo_标签{};
结构b_标签:foo_标签{};
结构d_标记:a_标记,b_标记{};
空f(foo_标签);
无效f(foo_标签);
//示例的其余部分是相同的。
//foo_标签:foo_标签是foo_标签的类型定义。

只是我所做工作的粗略概述:功能的多类层次结构。例如,为了方便起见,UnixApi和UnixUserland将Unix作为孩子使用。根据需要提供尽可能多的层次结构和细粒度。使用platform为当前平台提供作为要调用的标记,使用PlatformRequired作为函数的类型。PlatformRequires然后有一个sfinae构造函数,该构造函数被禁用,除非PlatformRequires中的所有类型都可以从PlatformProviders中转换。真的只是一个上午吗
struct foo_bar_tag {
    typedef foo_bar_tag foo_tag;
};

struct a_tag : foo_bar_tag {};
struct b_tag : foo_bar_tag {};
struct d_tag : a_tag, b_tag {};

template< typename has_foo_tag >
typename has_foo_tag::foo_tag
slice_foo_tag( has_foo_tag ) { return {}; }

void f( foo_bar_tag );
void f( foo_baz_tag );

template< typename full_tag >
void g( full_tag t ) {
    f( t ); // error, ambiguous base conversion
    f( slice_foo_tag( t ) ); // OK
    f( typename full_tag::foo_tag{} ); // OK
}
template< typename >
struct foo_tag {};

struct a_tag : foo_tag< bar > {};
struct b_tag : foo_tag< bar > {};
struct d_tag : a_tag, b_tag {};

void f( foo_tag< bar > );
void f( foo_tag< baz > );

// Rest of example is the same.
// foo_tag< bar >::foo_tag is a typedef to foo_tag< bar >.