[SWIG] quick start

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

  1. .\simple\example.h
#pragma once
//global variable
double Foo = 0;

//function
int gcd(int x, int y);
  1. .\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

  1. Right click i file > Properties > General > item type > select custom build tool
  2. Right click i file > attributes > custom build tool >
    1. Command line: swig exe -c++ -csharp %(FullPath)
    2. 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

Keywords: C++ csharp

Added by sleepydad on Tue, 14 Dec 2021 12:54:07 +0200