首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于标记调度的C++代码平台识别与仿真专用化

基于标记调度的C++代码平台识别与仿真专用化
EN

Stack Overflow用户
提问于 2015-11-22 07:27:45
回答 2查看 342关注 0票数 1

背景

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

由于平台集仍然是在编译时完全确定的,所以我可以使用模板专门化这样的技术来避免运行时开销。但是,潜在的代码库很大。对每个API使用类模板专门化(尤指。(免费功能)会使代码膨胀很多。函数模板(如果没有包装器直接使用)很容易导致重载混乱,也很难确定如何指定匹配顺序。因此,我认为使用标记分发可能会更好。

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

目前:

代码语言:javascript
复制
#ifdef _WIN32
#   define Platform_Win32 1
#endif

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

今后:

代码语言:javascript
复制
#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.

但事情并不总是那么简单。平台中立本身已经是个怪物..。

我可能需要:

代码语言:javascript
复制
struct POSIXTag : BaseTag // POSIX is based on ISO C, which is nearly the "base" of all C++ implementations, no need to dig deeper...
{};

struct UNIXTag : POSIXTag // SUS is based on POSIX.
{};

struct OSXTag : UNIXTag // Now OS X is a UNIX.
{};

还是没事..。但如果有些事情不太自信呢.?

代码语言:javascript
复制
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) ??

甚至(参见http://pubs.opengroup.org/onlinepubs/009695399/help/codes.html):

代码语言:javascript
复制
namespace POSIXTags
{

struct XSI : POSIXTag
{};

struct XSR : XSI
{};

struct SHM : POSIXTag
{};

struct AIO : POSIXTag
{};

struct BAR : POSIXTag
{};

struct CPT : POSIXTag
{};

struct CS : POSIXTag
{};

//...

}

或者更糟:

代码语言:javascript
复制
//...
struct POSIX2013Tag : POSIXTag // XXX: POSIX2008Tag? What about depercation and removal of APIs?
{};

#if _POSIX_SOURCE >= 200809
using POSIXTag = POSIX2013Tag;
#else
//...
#endif

叹息..。够了..。(到处都是有条件的包含,都是NG.)

我的问题

有什么先进的技术吗?

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

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

  • 如果是这样,主流实现(最近/主干版本的G++/Clang++/MSVC++等)可以像简单的std::iterator_tag案例那样优化运行时开销吗?
  • 如果没有,有没有其他解决方案来解决这里的背景问题,(更多)优雅?

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

  • 显式地保持基座不偶尔重复。这与简单的情况(如std::iterator_tag )非常不同,后者的层次结构设计是稳定的/在早期几乎是固定的,不太可能在以后(经常)进行扩展。(尽管存在提议的新迭代器类别设计。)
  • 允许用户不知道模板参数的顺序,以及在这里介绍的所有不同标识符之间表示的特征集/平台重叠的精确知识。如果用户没有使用这些特定于平台的接口,那么大多数用户甚至不应该被要求知道所有支持的平台的存在是很自然的。它还简化了这些特定于平台的API的维护工作。这可以通过提供像UnionOfPlatforms<A, B, C>这样的助手标记类型与UnionOfPlatforms<B, A, C>有效地等效来实现,以模仿#if Platform_A || Platform_B || Platform_C#if Platform_B || Platform_A || Platform_C之间的等价。在实现方面,这是一个重要的问题,因为C++重载解析(尤其是)。(完全匹配)规则是基于名义类型的。如果我偶尔弄乱了订单,很可能很难测试和调试。(如果Platform_BPlatform_C有一些共同的特性,或者甚至其中一个是另一个的子集,该怎么办?为使超载工作,不允许在基础上有歧义。)注意,这个问题的主要部分本质上是(编译时)元和类型的简化实例,C++类型系统一般不能很好地处理这个实例。

下面是关于第二点的更具体的“真实世界”示例:

代码语言:javascript
复制
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::integral_constant将宏值和标记链接在一起,并且假设所有与Wine 32兼容的平台都基于WinNT,没有Wine 16/Win9x/WinCE,而且我还没有考虑支持WinRT,甚至是葡萄酒.)

EN

回答 2

Stack Overflow用户

发布于 2016-01-29 15:56:37

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

代码语言:javascript
复制
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
}

如果标记基类型是模板化的,则可以免费获得成员类型类型(作为注入的类名)。

代码语言:javascript
复制
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 >.
票数 1
EN

Stack Overflow用户

发布于 2015-11-22 11:13:38

供参考:

您可能想看看GTK (或者您已经知道)它们是如何完成操作系统适配层的。这可能是个很好的参考。它宣传在Unix/Linux、Windows和OS/X上工作

http://www.gtk.org/download/

关于设计的

我在这里要做的是将问题分为API部分和实现部分。API将处理部分OS差异,而实现部分将处理其余部分。以及如何决定哪一个做什么?这来自于良好的API实践。

例如,OS版本依赖项将转到实现部分。还有一些非常相似的操作系统:es可以组合在一起。如果达到极限,您可以创建两个OS适配器: general和windows。:-)

关于虚拟成员的

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

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33852593

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档