5

我经常面临将一个 API 的参数空间映射到另一个 API 的参数空间的问题。我经常看到嵌套嵌套的嵌套 ... switch 语句解决了这个问题。

而且我想知道是否会碰巧有一个库或一种技术可以让您“声明”映射而不是“编程”它。

一个简单的例子是将两个枚举的值合并为一个:

namespace sourceAPI {
  struct A { typedef e { A1, A2, A3 } };
  struct B { typedef e { B1, B2 } };
}

namespace targetAPI {
  struct AB { typedef e { A1B1, A1B2, A2B1, A2B2, A3B1, A3B2 } };
}

其中映射通常像

switch( a ){
  case( A::A1 ): switch( b ) {
     case( B::B1 ): return A1B1;
     case( B::B2 ): return A1B2;
     ....
}

而且这个映射还需要一个“反向”开关。

但我更喜欢“密集”的东西

declare( source( A::A1, B::B1 ), target( AB::A1B1 ) );
declare( source( A::A1, B::B2 ), target( AB::A1B2 ) );
....

有没有人见过这样的技术或框架或库?

4

5 回答 5

3

您可以使用Boost.Bimap,它提供了两种类型之间的双向映射。

它有一些运行时开销(通常,与std::map为此目的使用一对 s 所获得的开销大致相同,这并不是很多)。

不过,它确实允许您定义与示例一样密集的映射;通常,您只需将配对添加到地图中,一次一对。

于 2009-12-07T15:32:44.050 回答
1

在许多情况下,您可以使用简单的查找表来完成此操作。由于枚举类型可以转换为整数值,因此您可以将它们用作不同类型枚举类型数组的索引,因此可以快速轻松地进行转换。它具有令人愉快的副作用,即尽可能快地制造这种东西。根据您的使用情况,查找表可能会变得相当大(但是,如果您要为每个枚举创建一个带有一个 case 的 switch 语句,那将会更大)。此外,如果您需要双向转换,那么您必须制作 2 个查找表,每个方向一个。

另外,请注意,许多编译器可以将枚举类型优化到存储每个值所需的最小数据类型。有一些方法可以解决这个问题(通常是编译器标志,或者您可以声明一个类似 0xffffffff 的“虚拟”值来强制进行 32 位枚举),但值得注意的是。

如果您有非整数值,则可以使用映射。STL 包括几个品种,正如其他人提到的,boost 有一个很好的双向品种。

于 2009-12-07T15:36:59.867 回答
1

使用表驱动的方法很好 - 它是等价的。

您需要担心两个问题:枚举布局更改(更改序数)或枚举内容更改(添加/删除)。

无论您选择哪种解决方案,您都希望减轻由这两个问题引起的问题。

在我自己的工作中,我更喜欢使用这样的模式:

TargetAPI ToTargetAPI(SourceAPI source)
{
    // ...
}

在事情可能嵌套的地方,我调用了另一个 ToXXXX 方法。

对于枚举,我的 ToXXXX 方法是单个开关或表查找(或者在某些情况下是表达式转换),并且我使用抛出的代码检查输入范围(无论是边界检查还是开关中的默认语句)。

对我来说,从一种类型转换为另一种类型的机制不如通过硬故障和快速故障来防止 API 更改时发生错误的工程重要。这样想:您是否会浪费更多时间输入完整的 switch 语句(嵌套或不嵌套)并进行错误检查或从超出范围且未检查的枚举中跟踪错误?

于 2009-12-07T15:41:24.540 回答
0

对于这种特定类型的任务,您通常可以作弊,并简单地对两个枚举使用不重叠的位模式。例如:

enum A::e a;
enum B::e b;

// ... set values of a and b.

AB::e result = (b << 2) | a;

在这种特殊情况下,由于 A 恰好有三个成员,因此结果甚至是一个连续的值范围。当成员的数量(除了一个枚举之外的所有)不是二的幂少一时,结果将是不连续的。

我很确定您的问题实际上是为了更笼统,而不仅仅是找到一种方法来处理这个特定问题。事实上,我怀疑这个例子纯粹是假设性的。不幸的是,很难猜测您可能关心的其他类型的问题。在 C++ 中肯定有很多声明式编程的例子。举几个明显的例子,几乎所有使用 Boost Spirit 或 Boost Xpressive 的东西最终都至少做了一些声明式编程。然而,无论好坏,它们都致力于类似的问题,而这些问题恰好与您似乎关心的问题有很大不同。

于 2009-12-07T16:02:33.613 回答
0

以 boost::tuple 作为键的 std::map。
如果您不介意在声明中使用“make_tuple”,那么您的声明中已经有可变键元素是免费的。您需要“make_tuple”来进行实际转换。

编辑:
当需要范围或通配符时,事情确实变得更加复杂

于 2009-12-07T16:47:51.717 回答