Text code address: https://github.com/geodoer/swig-examples/tree/main/A-HelloSWIG
Original address: https://www.yuque.com/cpptd/swig/gxz6qc
SWIG series notes: https://www.yuque.com/cpptd/swig
After reading this article, you can understand the principle of SWIG and how to use it simply.
A-HelloSWIG ├── simple | ├── example.h │ ├── example.cpp │ ├── example.i │ ├── simple.vcxproj │ └── simple.vcxproj.filters ├── usesimple | ├── Program.cs | └── usesimple.csproj └── A-HelloSWIG.sln
Create a new Visual studio project named HelloSWIG
Step 1: write cpp code
Create a sub project named simple and change the type to dynamic library
Writing C + + code
- .\simple\example.h
#pragma once //global variable double Foo = 0; //function int gcd(int x, int y);
- .\simple\example.cpp
#include"example.h" #include<iostream> /* Calculates the maximum common divisor of a positive integer */ int gcd(int x, int y) { int g; g = y; while (x > 0) { g = x; x = y % x; y = g; } return g; }
Compile the simple subproject to generate simple dll
Simple DLL contains example Relevant codes of H
Step 2: write the interface description file of SWIG
SWIG's interface description file is used to tell SWIG the export content, information and rules of the interface. The suffix is usually i
Our goal is to put example H is exposed to C# so that C# can call example h
Therefore, according to example H preparation i file (example.i)
/* Module name */ %module simple /* The following will be copied intact to*_ wrap. In cxx */ %{ //************************************** //SWIG Contents copied from interface file extern int gcd(int x, int y); extern double Foo; //************************************** %} /* Specify the header file to be parsed and generate the wrapper code */ %include"example.h"
Finished writing After i file, we can run SWIG from the command line to generate packaging code and interface code
swig.exe -c++ -csharp example.i
SWIG generated the following files
HelloSWIG ├── simple | ├── ... │ ├── example_wrap.cxx │ ├── simple.cs | └── simplePINVOKE.cs ├── ...
Let's look at the documents one by one SWIG Did something.<br /><br /> <a name="zIHBh"></a> #### example_wrap.cxx (wrapper) `example_wrap.cxx`Actually`SWIG`In the name`SW`. Simplified Wrapper Simple wrapper.<br />It will`example.h`The content in the simply encapsulates a layer, which will be C#(client language) use. ```cpp //..... // The first part is the fixed code of SWIG //************************************** //Contents copied from SWIG interface file extern int gcd(int x, int y); extern double Foo; //************************************** #ifdef __cplusplus extern "C" { #endif example.h Medium double Foo Global variables, SWIG Wrap it up //Setting function of Foo SWIGEXPORT void SWIGSTDCALL CSharp_Foo_set(double jarg1) { double arg1 ; arg1 = (double)jarg1; Foo = arg1; } //get function of Foo SWIGEXPORT double SWIGSTDCALL CSharp_Foo_get() { double jresult ; double result; result = (double)Foo; jresult = result; return jresult; } example.h Medium gcd,SWIG Wrap it up SWIGEXPORT int SWIGSTDCALL CSharp_gcd(int jarg1, int jarg2) { int jresult ; int arg1 ; int arg2 ; int result; arg1 = (int)jarg1; arg2 = (int)jarg2; result = (int)gcd(arg1,arg2); jresult = result; return jresult; } //....
simplePINVOKE.cs
simplePINVOKE.cs will load C + + related code from dll to call C + +.
class simplePINVOKE { protected class SWIGExceptionHelper { //... } protected static SWIGExceptionHelper swigExceptionHelper = new SWIGExceptionHelper(); public class SWIGPendingException { //... } protected class SWIGStringHelper { //... } static protected SWIGStringHelper swigStringHelper = new SWIGStringHelper(); static simplePINVOKE() { } //From simple DLL named CSharp_ Foo_ Function of set //Foo mapped to C#_ Set function [global::System.Runtime.InteropServices.DllImport("simple", EntryPoint="CSharp_Foo_set")] public static extern void Foo_set(double jarg1); //From simple Import CSharp from DLL_ Foo_ Get function //Foo mapped to C#_ Get function [global::System.Runtime.InteropServices.DllImport("simple", EntryPoint="CSharp_Foo_get")] public static extern double Foo_get(); //From simple Import CSharp from DLL_ gcd function, mapped to the gcd function of C# [global::System.Runtime.InteropServices.DllImport("simple", EntryPoint="CSharp_gcd")] public static extern int gcd(int jarg1, int jarg2); }
Here is a key point:
- Simple in DllImport("simple", EntryPoint="CSharp_Foo_set") is actually Module name in i file!
- So The module name of the i file needs to be the same as*_ wrap. The name of the dll where cxx is located is the same
simple.cs (interface file)
This file can be said to be I in the name of SWIG, i.e. Interface. It is an Interface file for c# using C + + code.
public class simple { public static double Foo { set { simplePINVOKE.Foo_set(value); } get { double ret = simplePINVOKE.Foo_get(); return ret; } } public static int gcd(int x, int y) { int ret = simplePINVOKE.gcd(x, y); return ret; } }
As you can see, this file is another agent layer.
- It will simplepinvoke CS encapsulates another layer and writes a simple class, the internal interface and example H exactly the same.
- In this way, when used in C # the interface is consistent in C + +!
A little can be found here. simple.cs name and The module name specified in i is the same.
summary
The overall logic is as follows:
example.h <-- example_wrap.cxx <-- example_wrapperPINVOKE.cs <-- example_wrapper.cs <-- C# code(client) C++code C++Agent layer C#from dll Loading in C++Function of C#Agent layer C#Call layer
Step 3: compile wrap Cxx file
In the above content (simplePINVOKE.cs), we found that the c# code generated by SWIG will search the module name. dll for the corresponding C + + code (such as global variables, functions and classes).
So, we're going to*_ wrap.cxx file compiled to module name dll.
Set example_wrap.cxx is added to the sample project and compiled again.
Step 4: C# call
Create a C# project (visual C# >. Net core > console application).
Add the C# files generated by SWIG (simple.cs, simplePINVOKE.cs)
Write test cases:
static void Main(string[] args) { Console.WriteLine("Hello World!"); // Call our gcd() function int x = 42; int y = 105; int g = cppproject.gcd(x, y); Console.WriteLine("The gcd of " + x + " and " + y + " is " + g); // Manipulate the Foo global variable // Output its current value Console.WriteLine("Foo = " + cppproject.Foo); // Change its value cppproject.Foo = 3.1415926; // See if the change took effect Console.WriteLine("Foo = " + cppproject.Foo); Console.ReadLine(); }
Then run.
Not surprisingly, your program will have an exception (in simple.cs)
System.TypeInitializationException:"The type initializer for 'simplePINVOKE' threw an exception." DllNotFoundException: Unable to load DLL 'simple' or one of its dependencies: The specified module could not be found. (Exception from HRESULT: 0x8007007E)
This is actually because the CSharp project cannot load simple DLL. We manually set simple Copy DLL to CSharp running directory (. \ A-HelloSWIG\usesimple\bin\Debug\netcoreapp2.1)
You can also configure post generation events for the 'simple' project and copy the generated dll to the specified directory
1. Right click 'simple' > generate event > post generation event
2. On the command line, enter: ` copy "$(OutDir)$(projectname).dll" "$(SolutionDir)csharpproject/bin/$(Configuration)/netcoreapp2.1"`
Attachment: Yes i file add compilation configuration
If you change the code, you have to open the command line and rerun the SWIG command, which is troublesome.
We can help you i file to add compilation configuration, so you don't have to run SWIG command on the command line
After writing, you need to do the following configuration
- Right click i file > Properties > General > item type > select custom build tool
- Right click i file > attributes > custom build tool >
- Command line: swig exe -c++ -csharp %(FullPath)
- Output:% (Filename)_wrap.cxx;%(Outputs)
After configuration, right-click example I file, click "compile"
In this way, you don't have to open the command line and run the command every time
Related concepts
C #, here, is called Target languages