美文网首页C++\CLI
托管的泛型与C++/CLI模板的配合

托管的泛型与C++/CLI模板的配合

作者: 左图右码 | 来源:发表于2022-04-18 20:56 被阅读0次

托管的泛型在dot Net framework中属于基础设施,被每个托管语言所支持,当然也被IL语言支持,但只有C++/CLI支持托管模板。
托管模板比泛型要方便得多,主要得益于编译期类型检查比运行期的检查更加方便。
也只有C++/CLI可以将托管模板翻译成托管泛型,就可以供其它托管语言(比如C#和VB)。但是,因为要进行类型实例化,所以不能避免代码膨胀的问题。
例子:

  • 制造接口
namespace test
{
    generic<typename T>
    public interface class IStack
    {
        void push(T);
        T pop();
        void print();  //打印栈内容,不是必选项
    };
}
  • 制作从接口继承的模板
    模版对接口进行了实现,用泛型返回特定的类型实现。
namespace test
{
    template<typename T>
    public ref class stack : IStack<T>
    {
        ref struct Node
        {
            T obj;
            Node^ next = nullptr;
            Node(T obj, Node^ top) : obj(obj), next(top) {}
        };
        Node^ Top = nullptr;
    public:
        virtual void push(T obj)
        {
            Top = gcnew Node(obj,Top);
        }
        virtual T pop()
        {
            if (Top)
            {
                auto obj = Top->obj;
                Top = Top->next;
                return obj;
            }
            return T();
        }
        virtual void print()
        {
            auto t = Top;
            while (t)
            {
                outputVal(t->obj);
                t = t->next;
            }
        }
    };
}

还可以为托管模板添加特化版本,dot Net泛型是不支持特化的。但不影响导出的时候进行识别。
下面是对System::String^的特化。

    template<>
    public ref class stack<String^> : IStack<String^>
    {
        cliext::vector<String^> data;
    public:
        virtual void push(String^ str)
        {
            data.push_back(str);
        }
        virtual String^ pop()
        {
            if (!data.empty())
            {
                auto bk = data.back();
                data.pop_back();
                return bk;
            }
            return nullptr;
        }
        virtual void print()
        {
            for(auto cur = data.rbegin();cur != data.rend();++cur)
                Console::WriteLine(*cur);
        }
    };
  • 制作泛型工厂类
namespace test
{
    public ref class stackMaker
    {
    public:
        generic<typename T>
        static IStack<T>^ CreateStack()
        {
            if (T::typeid == int::typeid)
            {
                return safe_cast<IStack<T>^>(gcnew stack<int>);
            }
            else if (T::typeid == String::typeid)
            {
                return safe_cast<IStack<T>^>(gcnew stack<String^>);
            }
            else if (T::typeid == double::typeid)
            {
                return safe_cast<IStack<T>^>(gcnew stack<double>);
            }
            else if (T::typeid == long::typeid)
            {
                return safe_cast<IStack<T>^>(gcnew stack<long>);
            }
            else if (T::typeid == char::typeid)
            {
                return safe_cast<IStack<T>^>(gcnew stack<char>);
            }
            else if (T::typeid == short::typeid)
            {
                return safe_cast<IStack<T>^>(gcnew stack<short>);
            }

            throw gcnew System::TypeAccessException("invalid type");
        }
    };
}

反编译后的IL如下:

cppcli-genericAndTemplate.png

总结:泛型仅仅是模板的包装,不是在运行阶段实例化,而是返回了编译阶段模板的实例化。不能避免代码的膨胀问题。通过利用托管泛型将模板导出为程序集接口,可以供其它托管语言使用,大大提高了代码复用。

相关文章

网友评论

    本文标题:托管的泛型与C++/CLI模板的配合

    本文链接:https://www.haomeiwen.com/subject/rcxjertx.html