2025, Oct 17 05:31
SWIG Warning 453 का हल: C‑Python array typemap, NumPy और carrays.i गाइड
SWIG में C से Python को array पास करते समय Warning 453 'No typemaps are defined' का हल: numpy.i व import_array() के साथ सही typemap, या carrays.i का साफ विकल्प।
SWIG के साथ C और Python को जोड़ना आम तौर पर सहज रहता है—जब तक कि array बीच में नहीं आते। एक मामूली-सी दिखने वाली बात “No typemaps are defined” चेतावनी तक ले जाती है और array आर्ग्युमेंट्स पहचाने ही नहीं जाते। नीचे संक्षेप में बताया गया है कि यह क्यों होता है और सही तरीका क्या है—NumPy के साथ भी और उसके बिना भी।
समस्या का न्यूनतम रूप
आपके पास एक सरल C फ़ंक्शन है जो किसी संख्यात्मक अनुक्रम का root-mean-square निकालता है, और आप उसे Python में एक्सपोज़ करना चाहते हैं। सब कुछ ठीक दिखता है, फिर भी SWIG चेतावनी देता है कि वह आपके array आर्ग्युमेंट्स के लिए typemap लागू नहीं कर पा रहा है।
// 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"
इस इंटरफ़ेस पर SWIG चलाने पर यह चेतावनी मिलती है:
Warning 453: Can't apply (double *IN_ARRAY1,int DIM1). No typemaps are defined.
असल में गड़बड़ी क्या है
IN_ARRAY1 और DIM1 नाम के type टोकन typemaps.i में परिभाषित नहीं हैं। वे numpy.i द्वारा उपलब्ध कराए जाते हैं। केवल typemaps.i शामिल करने पर SWIG को उन NumPy array typemap के बारे में पता नहीं चलता जिन्हें आप इस्तेमाल करना चाह रहे हैं, इसलिए वह आपके double* और int पैरामीटर पर array-संबंधित व्यवहार नहीं जोड़ पाता। नतीजा वही चेतावनी है कि आपकी मांग से मेल खाते typemap मौजूद नहीं हैं।
समाधान: numpy.i शामिल करें और उसे इनिशियलाइज़ करें
इन array typemap का उपयोग करने के लिए numpy.i शामिल करें और जेनरेटेड मॉड्यूल से NumPy C-API को इनिशियलाइज़ करें। नीचे दिया गया standalone इंटरफ़ेस वही RMS लॉजिक एक्सपोज़ करता है और array हैंडलिंग को सही तरह से जोड़ता है। सुविधा के लिए C फ़ंक्शन बॉडी को सीधे इंटरफ़ेस फ़ाइल में डाला गया है; चाहें तो इसे अलग compilation unit में रख सकते हैं।
%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);
Windows पर Microsoft टूल्स के साथ, नीचे दिया गया makefile मॉड्यूल बनाता है। अपने Python और NumPy इंस्टॉलेशन के पथ के अनुसार बदलाव करें।
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
बिल्ड करने के बाद, मॉड्यूल इम्पोर्ट करके Python सीक्वेंस के साथ फ़ंक्शन कॉल करना अपेक्षित रूप से काम करता है:
>>> import metrics
>>> metrics.rms_val([1, 2, 3])
NumPy के बिना विकल्प: carrays.i
यदि आप NumPy पर निर्भर नहीं रहना चाहते, तो आप SWIG के carrays.i हेल्पर्स का उपयोग करके Python से कच्चे C array बनाना और संभालना कर सकते हैं। इसमें आप नैटिव Python कंटेनर पास करने के बजाय जेनरेटेड फ़ंक्शंस के जरिए मेमोरी मैनेज करेंगे।
%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);
Python से जेनरेटेड हेल्पर्स का उपयोग इस तरह दिखता है:
>>> import rawbuf
>>> arr = rawbuf.new_doubleArray(3)
>>> for i in range(3):
...     rawbuf.doubleArray_setitem(arr, i, i + 1)
...
>>> rawbuf.rms_val(arr, 3)
यह क्यों मायने रखता है
C से Python बाइंडिंग में array पास करना एक अहम मोड़ होता है: इंटरफ़ेस या तो Python की भाषा में बात करता है या आपसे कच्ची मेमोरी से जूझने को कहता है। यहां की चेतावनी संकेत देती है कि चुनी गई typemap लाइब्रेरी स्कोप में नहीं थी। जब आप IN_ARRAY1 और DIM1 पर निर्भर हैं, तो numpy.i शामिल करके—या मैनुअल नियंत्रण के लिए carrays.i चुनकर—आप अनुबंध को स्पष्ट करते हैं और बाइंडिंग जेनरेशन के दौरान खामोश मिसमैच से बचते हैं।
निष्कर्ष
यदि SWIG कहे कि वह typemap लागू नहीं कर सकता, तो जांचें कि जिन typemap नामों का आप उपयोग कर रहे हैं, वे वास्तव में शामिल की गई फ़ाइलों में परिभाषित हैं या नहीं। IN_ARRAY1 और DIM1, typemaps.i में नहीं बल्कि numpy.i में मिलते हैं। Python डेटा को स्वाभाविक रूप से पास करने के लिए numpy.i शामिल करें और import_array() के जरिए NumPy C-API इनिशियलाइज़ करें। अगर आपके प्रोजेक्ट में NumPy उपयुक्त नहीं है, तो carrays.i आपको Python से कच्चे C array के साथ काम करने का स्पष्ट रास्ता देता है और पूरा नियंत्रण बनाए रखता है। सही typemap स्रोत आने के बाद, आपका RMS फ़ंक्शन—और इसी तरह की अन्य संख्यात्मक रूटीन—सभी प्लेटफ़ॉर्म पर साफ़, पूर्वानुमेय व्यवहार दिखाएंगे।
यह लेख StackOverflow पर एक प्रश्न (लेखक: daveed krizhey) और Mark Tolonen के उत्तर पर आधारित है।