본문 바로가기
C/SWIG

SWIG (C/C++와 다른 언어 연결하는 도구)

by yororing 2024. 6. 21.

00 개요

  • 목적: SWIG가 뭔지 정리하기 위함
  • 내용: SWIG의 정의, SWIG의 기본적인 작업, input file들의 구조, 표준 ANSI <American National Standards Institute (미국 국가표준 협회)> C declaration을 어떻게 처리하는지
  • SWIG 공식 문서의 "5 SWIG Basics"를 거의 그대로 적음

01 SWIG란

1. 정의

  • 'Simplified Wrapper and Interface Generator'
  • C/C++ 언어로 된 Declaration들을 다른 언어로 된 wrapper와 interface를 생성하는 무료 도구
  • SWIG is a free software development tool (an interface compiler) that takes C/C++ declarations and creates the wrappers needed to access those declarations from different languages
    • SWIG가 wrapper code 생성할 수 있는 언어들: C#, D, Go, Guile, Java, Javascript, Lua, MzScheme/Racket, OCaml, Octave, Perl, PHP, Python, R, Ruby, Scilab, Tcl
    • 더불어 the parse tree can be exported as XML
  • Possible applications of SWIG include:
    • Building interpreted interfaces to existing C programs.
    • Rapid prototyping and application development.
    • Interactive debugging.
    • Reengineering or refactoring of legacy software into scripting language components.
    • Making a graphical user interface (using Tk for example).
    • Testing of C libraries and programs (using scripts).
    • Building high performance C modules for scripting languages.
  • SWIG was originally designed to make it extremely easy for scientists and engineers to build extensible scientific software without having to get a degree in software engineering. Because of this, the use of SWIG tends to be somewhat informal and ad-hoc (e.g., SWIG does not require users to provide formal interface specifications as you would find in a dedicated IDL compiler). Although this style of development isn't appropriate for every project, it is particularly well suited to software development in the small; especially the research and development work that is commonly found in scientific and engineering projects. However, nowadays SWIG is known to be used in many large open source and commercial projects.

02 SWIG 실행

  • SWIG를 실행하기 위해선 swig 명령어를 옵션 및 파일명과 함께 사용
swig [options] filename
  • filename:
    • SWIG interface file 또는 C/C++ 헤더파일
  • options:
    • 아래는 모든 options의 일부. 각 언어마다 options 더 있음.
    • swig -help 또는 swig -lang -help 로 모든 option들 조회 가능. 
-allegrocl            Generate ALLEGROCL wrappers
-chicken              Generate CHICKEN wrappers
-clisp                Generate CLISP wrappers
-cffi                 Generate CFFI wrappers
-csharp               Generate C# wrappers
-guile                Generate Guile wrappers
-java                 Generate Java wrappers
-lua                  Generate Lua wrappers
-modula3              Generate Modula 3 wrappers
-mzscheme             Generate Mzscheme wrappers
-ocaml                Generate Ocaml wrappers
-perl                 Generate Perl wrappers
-php                  Generate PHP wrappers
-pike                 Generate Pike wrappers
-python               Generate Python wrappers
-r                    Generate R (aka GNU S) wrappers
-ruby                 Generate Ruby wrappers
-sexp                 Generate Lisp S-Expressions wrappers
-tcl                  Generate Tcl wrappers
-uffi                 Generate Common Lisp / UFFI wrappers
-xml                  Generate XML wrappers

-c++                  Enable C++ parsing
-Dsymbol              Define a preprocessor symbol
-Fstandard            Display error/warning messages in commonly used format
-Fmicrosoft           Display error/warning messages in Microsoft format
-help                 Display all options
-Idir                 Add a directory to the file include path
-lfile                Include a SWIG library file.
-module name          Set the name of the SWIG module
-o outfile            Name of output file
-outcurrentdir	      Set default output dir to current dir instead of input file's path
-outdir dir           Set language specific files output directory
-swiglib              Show location of SWIG library
-version              Show SWIG version number

 

1. Input format

  • input:
    • a SWIG Interface File containing ANSI C/C++ declarations and special SWIG directives (.i or .swg 확장자)
    • in certain cases, SWIG can be used directly on raw header files or source files.
  • SWIG Interface File의 가장 흔한 형식:
%module mymodule 
%{
#include "myheader.h"
%}
// Now list ANSI C/C++ declarations
int foo;
int bar(int x);
...
  • %module 지시문을 사용하여 생성될 module의 이름 지정 
  • %{ ... %} 안에 있는 모든 내용들은 SWIG로 생성되는 wrapper file에 그대로 복사됨  
    • This section is almost always used to include header files and other declarations that are required to make the generated wrapper code compile.
    • NOTE: SWIG  input 파일에 declataion을 포함시켰다고 하여 generated wrapper code에 declaration이 자동적으로 나타나지 않는다 → 그러므로 반드시 %{ ... %} section 안에 알맞은 헤더 파일들을 포함시켜야 한다.
    • It should be noted that the text enclosed in %{ ... %} is not parsed or interpreted by SWIG.
    • The %{...%} syntax and semantics in SWIG is analogous to that of the declarations section used in input files to parser generation tools such as yacc or bison

2. SWIG Output

  • output:
    • a C/C++ file that contains all of the wrapper code needed to build an extension module.
    • SWIG may generate some additional files depending on the target language.
    • By default, an input file with the name file.i is transformed into a file file_wrap.c or file_wrap.cxx (depending on whether or not the -c++ option has been used).
  • -o 옵션을 통해 output 파일의 이름 변경 가능
    • In certain cases, file suffixes are used by the compiler to determine the source language (C, C++, etc.).
    • Therefore, you have to use the -o option to change the suffix of the SWIG-generated wrapper file if you want something different than the default.
$ swig -c++ -python -o example_wrap.cpp example.i
  • 위 설명:
    • The C/C++ output file created by SWIG often contains everything that is needed to construct a extension module for the target scripting language.
    • SWIG is not a stub compiler nor is it usually necessary to edit the output file (and if you look at the output, you probably won't want to).
    • To build the final extension module, the SWIG output file is compiled and linked with the rest of your C/C++ program to create a shared library.
  • -outdir 옵션을 통해 output directory 설정 가능:
    • Many target languages will also generate proxy class files in the target language.
    • The default output directory for these language specific files is the same directory as the generated C/C++ file.
 $ swig -c++ -python -outdir pyfiles -o cppfiles/example_wrap.cpp example.i

 

  • 만약 cppfiles 및 pyfiles 디렉토리들이 존재한다면 다음이 생성됭 것임
cppfiles/example_wrap.cpp
pyfiles/example.py
  • -outcurrentdir (without -o) 옵션이 사용된 경우 SWIG는 일반적인 C/C++ 컴파일러와 같이 동작하며 output directory의 기본값은 현재 디렉토리가 됨
  • 이 옵션을 안 줄 시 output directory의 기본값은 input 파일의 경로임
  • -o 와 -outcurrentdir 옵션 동시 사용 시
    • -outcurrentdir is effectively ignored as the output directory for the language files is the same directory as the generated C/C++ file if not overidden with -outdir

3. Comments

  • C/C++ style comments may appear anywhere in interface files
  • In previous versions of SWIG, comments were used to generate documentation files
  • However, this feature is currently under repair and will reappear in a later SWIG release

4. C 전처리기 (Preprocessor)

0) 개요

  • SWIG includes its own enhanced version of the C preprocessor.
  • SWIG preprocessor supports the standard preprocessor directives and macro expansion rules.
  • However, a number of modifications and enhancements have been made.
  • 예를 들면,
    • #include statements are ignored unless the -includeall command line option has been supplied
      • The reason for disabling includes is that SWIG is sometimes used to process raw C header files
      • In this case, you usually only want the extension module to include functions in the supplied header file rather than everything that might be included by that header file (i.e., system headers, C library functions, etc.)
    • SWIG preprocessor skips all text enclosed inside a %{...%} block
    • SWIG preprocessor includes a number of macro handling enhancements that make it more powerful than the normal C preprocessor. These extensions are described in the "Preprocessor" chapter. (밑에 참조)

1) File Inclusion

  • SWIG interface에 파일을 include하기 위해선 %include 지시문(directives) 사용
  • #include와 달리 %include는 각 파일을 한 번만 include함 (and will not reload the file on subsequent %include declarations)

2) File Imports

  • SWIG provides another file inclusion directive w/ the %import directive
  • 예시)
  • %import의 목적: to collect certain info from another SWIG interface file or a header file w/o actually generating any wrapper code
  • such info generally includes type declarations (e.g., typedef) as well as C ++ classes that might be used as base-classes for class declarations in the interface
  • the use of %import is also important when SWIG is used to generate extensions as a collection of related modules
  • -importall directive tells SWIG to follow all #include statements as imports - this might be useful if you want to extract type definitions from system header files w/o generating any wrappers

3) Conditional Compilation

  • SWIG fully supports the use of #if, #ifdef, #ifndef, #else, #endif to conditionally include parts of an interface
  • The following symbols are predefined by SWIG when it is parsing the interface:
SWIG                            Always defined when SWIG is processing a file
SWIGIMPORTED                    Defined when SWIG is importing a file with %import
SWIGMAC                         Defined when running SWIG on the Macintosh
SWIGWIN                         Defined when running SWIG under Windows
SWIG_VERSION                    Hexadecimal number containing SWIG version,
                                such as 0x010311 (corresponding to SWIG-1.3.11).

SWIGALLEGROCL                   Defined when using Allegro CL
SWIGCFFI                        Defined when using CFFI
SWIGCHICKEN                     Defined when using CHICKEN
SWIGCLISP                       Defined when using CLISP
SWIGCSHARP                      Defined when using C#
SWIGGUILE                       Defined when using Guile
SWIGJAVA                        Defined when using Java
SWIGLUA                         Defined when using Lua
SWIGMODULA3                     Defined when using Modula-3
SWIGMZSCHEME                    Defined when using Mzscheme        
SWIGOCAML                       Defined when using Ocaml
SWIGOCTAVE                      Defined when using Octave
SWIGPERL                        Defined when using Perl
SWIGPHP                         Defined when using PHP
SWIGPIKE                        Defined when using Pike
SWIGPYTHON                      Defined when using Python
SWIGR                           Defined when using R
SWIGRUBY                        Defined when using Ruby
SWIGSEXP                        Defined when using S-expressions
SWIGTCL                         Defined when using Tcl
SWIGXML                         Defined when using XML
  • In addition, SWIG defines the following set of standard C/C++ macros:
__LINE__                        Current line number
__FILE__                        Current file name
__STDC__                        Defined to indicate ANSI C
__cplusplus                     Defined when -c++ option used
  • Interface files can look at these symbols as necessary to change the way in which an interface is generated or to mix SWIG directives w/ C code
  • These symbols are also defined within the C code generated by SWIG (except for the symbol `SWIG' which is only defined within the SWIG compiler)

4) Macro Expansion

  • Traditional preprocessor macros can be used in SWIG interfaces. 
  • Be aware that the #define statement is also used to try and detect constants. 
  • Therefore, if you have something like this in your file,
#ifndef _FOO_H 1
#define _FOO_H 1
...
#endif
  • you may get some extra constants such as _FOO_H showing up in the scripting interface.
  • More complex macros can be defined in the standard way.
  • 예시)
#define EXTERN extern
#ifdef __STDC__
#define _ANSI(args)   (args)
#else
#define _ANSI(args) ()
#endif
  • The following operators can appear in macro definitions:
    • #x → Converts macro argument x to a string surrounded by double quotes ("x")
    • x ## y   Concatenates x and y together to form xy.
    • `x`   If x is a string surrounded by double quotes, do nothing. Otherwise, turn into a string like #x. This is a non-standard SWIG extension.

5) SWIG Macros

  • SWIG provides an enhanced macro capability with the %define and %enddef directives(지시문)
  • 예시)
%define ARRAYHELPER(type,name)
%inline %{
type *new_ ## name (int nitems) {
   return (type *) malloc(sizeof(type)*nitems);
}

void delete_ ## name(type *t) {
   free(t);
}

type name ## _get(type *t, int index) {
   return t[index];
}

void name ## _set(type *t, int index, type val) {
   t[index] = val;
}
%}
%enddef

ARRAYHELPER(int, IntArray)
ARRAYHELPER(double, DoubleArray)
  • The primary purpose of %define is to define large macros of code. Unlike normal C preprocessor macros, it is not necessary to terminate each line with a continuation character (\)--the macro definition extends to the first occurrence of %enddef.
  • Furthermore, when such macros are expanded, they are reparsed through the C preprocessor.
  • Thus, SWIG macros can contain all other preprocessor directives except for nested %define statements.
  • The SWIG macro capability is a very quick and easy way to generate large amounts of code.
  • In fact, many of SWIG's advanced features and libraries are built using this mechanism (such as C++ template support).

6) C99 and GNU Extensions

7) Preprocessing and %{...%} & "..." delimiters

  • SWIG 전처리기는 %{...%} 코드 블록 안에 포함됨 텍스트들을 처리하지 않음
  • 그러므로 다음과 같이 코드 작성 시:
%{
#ifdef NEED_BLAH
int blah() {
    ...
}
#endif
%}
  • %{...%} 블록 안에 내용들은 modification 없이 output (including all preprocessor directives)에 복사됨

8) Preprocession and {...} delimiters

  • SWIG always runs the preprocessor on text appearing inside { ... }. 
  • However, sometimes it is desirable to make a preprocessor directive pass through to the output file.
  • 예시)
%extend Foo {
    void bar() {
        #ifdef DEBUG
        printf("I'm in bar\n");
        #endif
    }
}
  • 기본값으로, SWIG will interpret the #ifdef DEBUG statement
  • However, if you really wanted that code to actually go into the wrapper file, prefix the preprocessor directives w/ % like this:
%extend Foo {
    void bar() {
        %#ifdef DEBUG
        printf("I'm in bar\n");
        %#endif
    }
}
  • SWIG will strip the extra % and leave the preprocessor directive in the code

9) Preprocessor and Typemaps

  • Typemaps support a special attribute called noblock where the { ... } delimiters can be used, but the delimiters are not actually generated into the code
  • The effect is then similar to using "" or %{ %} delimiters but the code is run through the preprocessor.
  • 예시)
// example 1
#define SWIG_macro(CAST) (CAST)$input
%typemap(in) Int {$1= SWIG_macro(int);}

// the above might generate
{
    arg1=(int)jarg1;
}

// example 2
#define SWIG_macro(CAST) (CAST)$input
%typemap(in,noblock=1) Int {$1= SWIG_macro(int);}

// the above might generate
    arg1=(int)jarg1;

// example 3
#define SWIG_macro(CAST) (CAST)$input
%typemap(in) Int %{$1=SWIG_macro(int);%}

// the above would generate
    arg1=SWIG_macro(int);

10) Viewing Preprocessor Output

  • like many compilers, SWIG supports a -E command line option to display the output from the preprocessor
  • When the -E switch is used, SWIG will not generate any wrappers
  • Instead, the results after the preprocessor has run are displayed
  • This might be useful as an aid to debugging and viewing the results of macro expansions.

11) The #Error and #Warning Directives

  • #warning directive will cause SWIG to issue a warning then continue processing
  • #error directive will cause SWIG to exit with a fatal error
  • 예시)
#error "This is a fatal error message"
#warning "This is a warning message"
  •  -cpperraswarn command line 옵션이 사용될 경우 the #error behaviour can be made to work like #warning 
  • Alternatively, the #pragma directive can be used to the same effect
  • 예시)
  /* Modified behaviour: #error does not cause SWIG to exit with error */
  #pragma SWIG cpperraswarn=1
  /* Normal behaviour: #error does cause SWIG to exit with error */
  #pragma SWIG cpperraswarn=0

5. SWIG Directives

6. Parser Limitations

 

03 Wrapping Simple C Declarations

1. 기본적인 Type 처리

2. Global Variables

3. Constants

4. const에 관하여

5. char *에 관하여

04 Pointers and Complex Objects

1. Simple 포인터

2. Run time 포인터 Type Checking

3. 파생 (Derived) Types, Structs, Classes

  • SWIG는 다른 모든 것들 (i.e., structs, classes, arrays, etc.) 을 포인터로 인식
  • 즉, SWIG manipulates everything else by reference
  • 예시)
%module fileio
FILE *fopen(char *, char *);
int fclose(FILE *);
unsigned fread(void *ptr, unsigned size, unsigned nobj, FILE *);
unsigned fwrite(void *ptr, unsigned size, unsigned nobj, FILE *);
void *malloc(int nbytes);
void free(void *);
  • In this file, SWIG는 FILE이 뭔지 모르나, 포인터로 사용되었기에 FILE이 뭔지 실제론 중요하지 않게 여김
  • 이 모듈을 Python으로 wrap 하였다면 you can use the functions just like you expect
# Copy a file
def filecopy(source, target):
    f1 = fopen(source, "r")
    f2 = fopen(target, "w")
    buffer = malloc(8192)
    nbytes = fread(buffer, 8192, 1, f1)
    while (nbytes > 0):
        fwrite(buffer, 8192, 1, f2)
        nbytes = fread(buffer, 8192 , 1, f1)
    free(buffer)
  • In this case, f1, f2 and buffer are all opaque objects containing C pointers
  • it doesn't matter what value they contain - our program works just fine w/o this knowledge

4. Undefined Datatypes

5. Typedef

05 Other Practicalities

1. Passing Structures by Value

2. Return by Value

3. Linking to Structure Variables

4. Linking to char *

5. Arrays

6. Creating read-only Variables

7. Renaming and Ignoring Declarations

8. Default/Optional Arguments

9. 포인터 to 함수 및 Callbacks

06 Structures and Unions

1. Typedef 및 Structures

2. Character Strings 및 Structures

3. Array Members

4. Structure Data Members

5. C Constructors 및 Destructors

6. Adding Member Functions to C Structures

7. Nested Structures

8. Structure Wrapping에 관하여

07 Code Insertion

  • Sometimes it is necessary to insert special code into the resulting wrapper file generated by SWIG. 
  • 예) you may want to include additional C code to perform initialization or other operations
  • 4 common ways to insert code 존재, but it's useful to know how the output of SWIG is structured first.

1. SWIG의 Output

  • SWIG의 output file은 크게 4 부분으로 나뉘어져있음 (다음 순으로): runtime code, headers, wrapper functions, and module initialization
  • Begin section: A placeholder to put code at the beginning of the C/C++ wrapper file
  • Runtime code: This code is internal to SWIG and is used to include type-checking and other support functions that are used by the rest of the module
  • Header section: This is user-defined support code that has been included by the %{ ... %} directive. Usually this consists of header files and other helper functions
  • Wrapper code: These are the wrappers generated automatically by SWIG
  • Module initialization: The function generated by SWIG to initialize the module upon loading.

2. Code Insertion Blocks

  • Code is inserted into the appropriate code section by using one of the code insertion directives listed below
  • The order of the sections in the wrapper file is as shown:
%begin %{
   ... code in begin section ...
%}

%runtime %{
   ... code in runtime section ...
%}

%header %{
   ... code in header section ...
%}

%wrapper %{
   ... code in wrapper section ...
%}

%init %{
   ... code in init section ...
%}
  • bare %{ ... %} directive는 %header %{ ... %}와 동일한 역할을 
  • %begin section is effectively empty as it just contains the SWIG banner by default
    • This section is provided as a way for users to insert code at the top of the wrapper file before any other code is generated
    • Everything in a code insertion block is copied verbatim into the output file and is not parsed by SWIG.
    • Most SWIG input files have at least one such block to include header files and support C code.
    • Additional code blocks may be placed anywhere in a SWIG file as needed.
%module mymodule
%{
#include "my_header.h"
%}
... Declare functions here
%{

void some_extra_function() {
  ...
}
%}
  • A common use for code blocks is to write "helper" functions. 
    • These are functions that are used specifically for the purpose of building an interface, but which are generally not visible to the normal C program.
    • 예시)
%{
/* Create a new vector */
static Vector *new_Vector() {
	return (Vector *) malloc(sizeof(Vector));
}

%}
// Now wrap it 
Vector *new_Vector();

3. Inlined Code Blocks

  • Since the process of writing helper functions is fairly common, there is a special inlined form of code block that is used as follows:
%inline %{
/* Create a new vector */
Vector *new_Vector() {
	return (Vector *) malloc(sizeof(Vector));
}
%}
  • %inline directive inserts all of the code that follows verbatim into the header portion of an interface file
  • The code is then parsed by both the SWIG preprocessor and parser
  • Thus, the above example creates a new command new_Vector using only one declaration
  • Since the code inside an %inline %{ ... %} block is given to both the C compiler and SWIG, it is illegal to include any SWIG directives inside a %{ ... %} block.

4. Initialization Blocks

  • When code is included in the %init section, it is copied directly into the module initialization function
  • 예시) if you needed to perform some extra initialization on module loading, you could write this:
%init %{
	init_variables();
%}

08 An Interface Building Strategy

1. Preparing a C Program for SWIG

2. SWIG Interface File

3. Separate Interface File 사용 이유

4. 알맞은 헤더 파일 가져오기

5. main()으로 할 것

 

참조

  1. https://www.swig.org/Doc4.2/SWIGDocumentation.pdf
  2. https://www.swig.org/Doc1.3/SWIG.html#SWIG_nn47 
  3. (Preprocessor) https://www.swig.org/Doc1.3/Preprocessor.html#Preprocessor
  4.  

'C > SWIG' 카테고리의 다른 글

SWIG 라이브러리  (0) 2024.06.24