2025, Oct 17 05:00

How to Resolve SWIG Array Typemap Errors in C-to-Python Bindings: numpy.i vs carrays.i Explained

Learn why SWIG shows 'No typemaps are defined' (Warning 453) for C arrays and how to fix it using numpy.i and import_array(), or avoid NumPy with carrays.i.

Bridging C and Python with SWIG often starts smoothly until arrays enter the picture. A deceptively small detail can lead to the dreaded “No typemaps are defined” warning and block array arguments from being recognized. Below is a concise walkthrough of what triggers this and how to fix it correctly, with and without NumPy.

The minimal issue

You have a simple C function that computes the root-mean-square of a numeric sequence and you want to expose it to Python. Everything looks reasonable, yet SWIG warns it can’t apply the typemap for your array arguments.

// compute.c
#include <math.h>
#include "compute.h"
double rms_val(double* buf, int count) {
    double total = 0.0;
    for (int i = 0; i < count; i++) {
        total += buf[i] * buf[i];
    }
    return sqrt(total / count);
}
// compute.h
#ifndef COMPUTE_H
#define COMPUTE_H
double rms_val(double* buf, int count);
#endif
// compute.i
%module compute
%include "typemaps.i"
%{
#include "compute.h"
%}
%apply (double *IN_ARRAY1, int DIM1) { (double *buf, int count) };
%include "compute.h"

Running SWIG on this interface produces the warning:

Warning 453: Can't apply (double *IN_ARRAY1,int DIM1). No typemaps are defined.

What is actually wrong

The type tokens IN_ARRAY1 and DIM1 are not defined in typemaps.i. They are provided by numpy.i. Including only typemaps.i leaves SWIG unaware of the NumPy array typemaps you are trying to use, so it cannot attach array behavior to your double* and int parameters. The result is the warning that no typemaps match your request.

Solution: include numpy.i and initialize it

To use those array typemaps, include numpy.i and initialize the NumPy C-API from the generated module. The following standalone interface exposes the same RMS logic and properly wires up the array handling. It embeds the C function body directly in the interface file for convenience, but you can keep it in a separate compilation unit as well.

%module metrics
%{
#define SWIG_FILE_WITH_INIT
#include <math.h>
double rms_val(double* buf, int count) {
    double total = 0.0;
    for (int i = 0; i < count; i++) {
        total += buf[i] * buf[i];
    }
    return sqrt(total / count);
}
%}
%include "numpy.i"
%init %{
    import_array();
%}
%apply (double *IN_ARRAY1, int DIM1) { (double *buf, int count) };
double rms_val(double* buf, int count);

On Windows with Microsoft tools, the following makefile builds the module. Adjust paths to match your Python and NumPy installation.

all: _metrics.pyd
metrics_wrap.c metrics.py: metrics.i
	echo swigging...
	swig -python metrics.i
_metrics.pyd: metrics_wrap.c
	cl /nologo /LD /MD /W3 /Fe_metrics.pyd /Ic:\python313\include /IC:\Python313\Lib\site-packages\numpy\_core\include metrics_wrap.c -link /libpath:c:\python313\libs

After building, importing the module and calling the function with a Python sequence works as expected:

>>> import metrics
>>> metrics.rms_val([1, 2, 3])

Alternative without NumPy: carrays.i

If you prefer not to depend on NumPy, you can use SWIG’s carrays.i helpers to construct and manipulate raw C arrays from Python. You will manage memory through generated functions rather than passing native Python containers.

%module rawbuf
%{
#include <math.h>
double rms_val(double* buf, int count) {
    double total = 0.0;
    for (int i = 0; i < count; i++) {
        total += buf[i] * buf[i];
    }
    return sqrt(total / count);
}
%}
%include <carrays.i>
%array_functions(double, doubleArray)
double rms_val(double* buf, int count);

Using the generated helpers from Python looks like this:

>>> import rawbuf
>>> arr = rawbuf.new_doubleArray(3)
>>> for i in range(3):
...     rawbuf.doubleArray_setitem(arr, i, i + 1)
...
>>> rawbuf.rms_val(arr, 3)

Why this matters

Array passing is a crossroads in C-to-Python bindings: the interface either speaks in terms Python understands or asks you to juggle raw memory. The warning here is a signal that the selected typemap library was not in scope. By including numpy.i when you rely on IN_ARRAY1 and DIM1, or choosing carrays.i when you want manual control, you make the contract explicit and avoid silent mismatches during binding generation.

Takeaways

If SWIG says it cannot apply a typemap, check that the typemap names you are using are actually defined by the files you include. IN_ARRAY1 and DIM1 live in numpy.i, not typemaps.i. Include numpy.i and initialize the NumPy C-API via import_array() to pass Python data in naturally. If NumPy is out of scope for your project, carrays.i provides a clean path to work with raw C arrays from Python while preserving full control. Either way, once the correct typemap source is in place, your RMS function—and any similar numeric routines—will expose clean, predictable behavior across platforms.

The article is based on a question from StackOverflow by daveed krizhey and an answer by Mark Tolonen.