• 用C++/CLI搭建C++和C#之间的桥梁


     
    一、简单用法
    C#和C++是非常相似的两种语言,然而我们却常常将其用于两种不同的地方,C#得益于其简洁的语法和丰富的类库,常用来构建业务系统。C++则具有底层API的访问能力和拔尖的执行效率,往往用于访问底层模块和构建有性能要求的算法。
    这两种场景看起来有较大的差异,大多数的时候可以各行其道。但还是有很多时候会出现融合的情况。当我们构建分布式系统的时候,由于RPC机制一般都是语言无关的,我们大可以将其各尽所长,按需划分在最能发挥其长处的位置。然而,一旦我们需要构建融合两者需求的集中式系统的时候,就会头痛无比。
    此时,我们可以使用C++/CLI搭建C++和.Net之间的桥梁,C++/CLI是一个比较有意思的两栖模块,它具有如下特点
    既可以访问.Net类库,也可以访问C++原生类库
    既可以被.Net程序引用,也可以被C++原生程序引用
    使用C++/CLI,我们可以使用C++编写算法,用C#编写界面,也可以使用.Net Framework类库增强C++程序功能,各取所长。关于的优点,园子里有篇文章介绍的比较详细,值得一读:从C++到C++/CLI。
    下面我们就以一个简单的例子来演示一下它的用法:
    Calculator.h:
        #pragma once
        namespace CppCliTest
        {
            public ref class Calculator
            {
            public:
                int Add(int a, int b);
            };
        }
     
    Calculator.cpp
        #include "stdafx.h"
        #include "Calculator.h"
        namespace CppCliTest
        {
            int Calculator::Add(int a, int b)
            {
                return a + b;
            }
        }
     
    main.cpp
        #include "stdafx.h"
        #include "Calculator.h"
        using namespace System;
        using namespace CppCliTest;
        int main(array<System::String ^> ^args)
        {
            Calculator^ calculator = gcnew Calculator();
            int result = calculator->Add(3, 2);
            Console::WriteLine(L"Result is {0}", result);
            return 0;
        }
     
    从这个例子中,我们可以简单的管中窥豹的看看C++/CLI是在C++的基础上扩充了一套语法,使其具有访问.Net原始的功能,这里用到的有:
    使用ref class声明CLI引用类型(C#中的class)
    使用^(例如如这里的String ^)来定义CLI引用类型
    使用gcnew创建CLI的引用类型
    具体的功能我将在后面的文章中再做介绍,MSDN中也有文档详细的介绍了这些语法:https://msdn.microsoft.com/zh-cn/library/ms235289.aspx
    虽然C++/CLI同时具有两者的功能,但它使得本就比较复杂的C++语法变得更加复杂了(特别是初期的版本,非常复杂,现在已经简化了不少了),并且长期没有得到VisualStudio这宇宙第一IDE的较好支持(在VS2010的时候还不支持智能提示),是无法与拥有大量语法糖的C#比开发效率的。加上大多数需求场景可以通过分布式系统解决,这些都导致了它一直没有得到太多的关注。但是,微软还是在积极的改进它的,加上C++11的支持,现在已经比之前好用多了,如果用在合适的位置,是绝对能让你的开发如鱼得水的。
    二、复杂用法
    对于如下C#代码:
    System.Object x = new System.Object();
    其在C++/CLI中的等价代码如下:
     
    System::Object^ x = gcnew System::Object();
    和传统的C++创建的语法比较下,
      
     P* x = new P();
     
    我们不难发现,对于托管对象,主要引入了如下两个语法:
    用gcnew代替new实现托管对象的创建
    用^代替*实现托管对象的指针
    这种方式创建的对象是可以直接被CLR支持的,可以在C#中使用。托管对象指针使用的方式和传统的对象指针还是比较类似的,直接使用->即可:
        System::Object^ x = gcnew System::Object();
        auto str = x->ToString();
    另外,C++/CLI也有一种类似于C++的对托管对象的引用的语法:
        System::Object^ x = gcnew System::Object();
        System::Object% y = *x;
        auto str = y.ToString();
    由于这种方式在C#里没有对应的语法,用起来感觉怪怪的,也不方便于其它.net语言集成。
     
    托管类型的定义
    我们也可以自定义托管类型,在CLR中,托管类型是分为引用类型(class)和值类型(struct)的,在C++/CLI中的分别定义方式如下:
    引用类型:
        public ref class MyClass
        {
        };
    值类型:
        public value class MyClass
        {
        };
    在ISO C++中类定义中加上了ref或value标记为托管类型,还算比较容易使用。
     
    枚举
    枚举的定义和C++11的enum class一样,它像数字那样可以同时应用于托管类型和非托管类型。
       
    public enum class SomeColors { Red, Yellow, Blue };
    或者更精确的表示:
    public enum class SomeColors : char { Red, Yellow, Blue };
     
    数组
    C++/CLI中新增了array<T> ^的方式定义数组。
    array<int> ^a = gcnew array<int>(100) { 1, 2, 3 };
    或者使用它的完整版:
    cli::array<int> ^a = gcnew cli::array<int> {1, 2, 3};
    不定参数
    对于C#中的不定参数的语法:
    void foo(params string[] args)
    在C++/CLI中对应的版本为:
    void foo(... array<String^>^ args)
     三、基本类型
    数值类型
    对于基本的数值类型,在C++/CLI中是可以直接映射为托管类型的数值的,可以同时应用于托管类型和非托管类型,编译器会将其自动转换。
     
    基本类型
    System命名空间中对应的类
    注释/用法
    bool
    System::Boolean
    bool dirty = false;
    char
    System::SByte
    char sp = ' ';
    signed char
    System::SByte
    signed char ch = -1;
    unsigned char
    System::Byte
    unsigned char ch = '';
    wchar_t
    System::Char
    wchar_t wch = ch;
    short
    System::Int16
    short s = ch;
    unsigned short
    System::UInt16
    unsigned short s = 0xffff;
    int
    System::Int32
    int ival = s;
    unsigned int
    System::UInt32
    unsigned int ui = 0xffffffff;
    long
    System::Int32
    long lval = ival;
    unsigned long
    System::UInt32
    unsigned long ul = ui;
    long long
    System::Int64
    long long etime = ui;
    unsigned long long
    System::UInt64
    unsigned long long mtime = etime;
    float
    System::Single
    float f = 3.14f;
    double
    System::Double
    double d = 3.14159;
    long double
    System::Double
    long double d = 3.14159L;
     
    字符串
    字符串CLI已经内置了:System::String,但C++的常用字符串有char*、wchar_t*、std::string等好多种,编译器提供了char*、wchar_t*到System::String的自动转换:
        System::String^ s = "hello worold";
        System::String^ s2 = L"hello worold";
    另外,也可以使用gcnew创建托管字符串:
        System::String^ s = gcnew String("hello worold");
    但是,对于System::String转char*,系统没有直接的语法支持。方法有很多种,我通常使用如下方式来转换:
        IntPtr ip = Marshal::StringToHGlobalAnsi(str);
        const char* ch = static_cast<const char*>(ip.ToPointer());
        //do something with ch
        Marshal::FreeHGlobal(ip);
    这里有个需要注意的地方是在使用完转换出来的const char*后需要释放掉转换过程中的Intptr,如果没有太多需要考虑性能的地方,大可以使用一个std::string将其拷贝走,写成如下函数形式: 
    #include <string>
        using namespace std;
        using namespace System;
        using namespace System::Runtime::InteropServices;
        string cast_to_string(String^ str)
        {
            IntPtr ip = Marshal::StringToHGlobalAnsi(str);
            const char* ch = static_cast<const char*>(ip.ToPointer());
            string stdStr = ch;
            Marshal::FreeHGlobal(ip);
            return stdStr;
        }
    四、网络资源
    关于C++/CLI的基础,我前面已经写过了几篇文章介绍过一些了,不过这些基本上都是管中窥豹,如果要详细了解C++/CLI,MSDN无疑是最好的教程。
    使用 C++ 互操作(隐式 PInvoke)
    Visual C++ 中的 .NET 编程
    如果需要在MFC中使用.net控件的话,可以参考如下三篇文章:
    在 MFC 对话框中承载 Windows 窗体用户控件
    以 MFC 视图的形式承载 Windows 窗体用户控件
    以 MFC 对话框的形式承载 Windows 窗体用户控件
    此外,MSDN文章如何实现 C++ 互操作 上有更加详细的文章索引。后面有空的话,我会继续继写一些相关的介绍文章的。
  • 相关阅读:
    Hadoop and net core a match made in docker
    JavaScript封装方法,兼容参数类型为Number和String
    HTML的input类型为hidden导致无法reset改字段的value问题
    MyBatis自动生成Java/C#的Bean(Entity)的等价MYSQL实现函数
    JMX configuration for Tomcat
    Resolved validation conflict with readonly
    FreeMarker example all in one
    Notepad++ 大小写转换
    常用编辑器列模式快捷键
    Redis应用一例(存证数量用计数器实现)
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/13962736.html
Copyright © 2020-2023  润新知