【问题标题】:Create an alias from one type to another创建从一种类型到另一种类型的别名
【发布时间】:2017-09-25 16:25:22
【问题描述】:

我正在用 c++ 编写某种 DI 容器,我很好奇是否可以在现代 c++ 中创建从一种类型到另一种类型的别名。 我基本上想要做的是能够通过它的别名接口调用实现构造函数。像这样:

di::Register<Interface, Impl>();
di::Resolve<Interface>(); // -> Impl should be resolved

问题是到目前为止,我还没有找到在编译时为 Interface 和 Impl 设置别名的方法。我可以使用 RTTI 做到这一点,但我真的不想使用它。有可能吗?

【问题讨论】:

  • 阅读[Boost].DI之类的内容可能会很有趣。
  • 我实际上在我的DI container library 中使用kgr::Default 做了一些非常相似的东西
  • @GuillaumeRacicot 是的,但我不想在这种情况下使用继承,我认为它会使接口变得非常庞大。
  • @s0nicYouth 继承是我选择的实现方式。但是如果你稍微修改一下代码,你也可以用 using 语句做同样的事情。

标签: c++ c++11 metaprogramming


【解决方案1】:

通过查看代码的界面,如果你有一个全局状态(我不积极推荐),你应该摆脱它:

using type_id_t = void(*)();
template<typename> void type_id() {}

struct di {
    using create_function_t = void*(*)();
    static std::unordered_map<type_id_t, create_function_t> types;

    template<typename I, typename T>
    static void Register() {
        types.emplace(type_id<I>, []{
            return static_cast<void*>(
                static_cast<I*>(new T)
            );
        });
    }

    template<typename I>
    static std::unique_ptr<I> Resolve() {
        return std::unique_ptr<I>{static_cast<I*>(types[type_id<I>]())};
    }
};

Live example

【讨论】:

  • 是的,你是对的。我将在这里使用全局状态。虽然我的目标是实现非常接近 dagger2 的东西。感谢您的片段,但正如我所提到的,我正在寻找一种在编译时实现它的方法。类似于使用 I = T。但无论如何,您的解决方案是 RTTI 的一个很好的替代品。
  • 我不推荐使用全局状态。尤其是对于 DI。这段代码在没有任何全局状态的情况下使用起来非常简单。只需删除所有静态关键字,瞧。
  • 我明白了。但对我来说,这里的全球状态不是问题。我的问题的主要目标是找到在编译时绑定类型的方法。不幸的是,即使我删除了全局状态,您的解决方案也不会这样做。
【解决方案2】:

问题是到目前为止,我还没有找到在编译时为 Interface 和 Impl 设置别名的方法。我可以使用 RTTI 做到这一点,但我真的不想使用它。有可能吗?

假设我正确理解了您的目标,您可以使用一个技巧来做到这一点。它看起来不像你那里的那么好:

http://stackoverflow.com/questions/4790721/c-type-registration-at-compile-time-trick

这个技巧归功于 Matt Calabrese,他在a Boostcon talk in like 2011 中描述了它。

据我所知,这个技巧符合标准,但您必须非常小心——如果您确实在某些头文件中开始注册,然后在某些 CPP 文件中继续注册,那么您可能会导致 ODR 违规很粗心。

我认为你最终会得到一些宏,例如

REGISTER_PAIR( interface, impl );

然后你会有一些类型别名,例如

get_registered_impl_t<interface>

解析为impl

在示例中,他们展示了如何制作随时间累积的类型列表。在您的情况下,它将是类型级“对”的列表,您可以通过线性扫描来搜索它。您可以尝试使用花哨的编译时映射数据结构,但在大多数情况下它没有意义,因为线性扫描足够快,而且您可以制作的列表长度受编译器的最大模板实例化深度的限制,通常是 100 或 200 或其他东西。如果您需要更大的列表,可以使用一些技巧,但我不会在这里详细说明。

【讨论】:

    猜你喜欢
    • 2021-04-11
    • 2011-05-30
    • 2011-10-02
    • 2020-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多