How to convert a python 2 C extension module to python 3 - python-c-api

I am trying to compile an extension module where I define a custom type. However, I got hold of some python 2 code for this purpose, but I cannot for the life of me convert it into a python 3 module.
So far, I have tweaked the original code I found in the book 'Python in a nutshell' in order to attempt to compile it for python 3, by merging some bits from https://docs.python.org/3/extending/newtypes_tutorial.html,but I am still getting nowhere. I know the 3rd edition of this book has a python 3 version of the code I am trying to compile but I don't have access to that edition of the book.
So, here is the module source code test.c:
#include "Python.h"
#include "structmember.h"
/* per-instance data structure */
typedef struct {
PyObject_HEAD
int first, second;
} intpair;
static int
intpair_init(PyObject *self, PyObject *args, PyObject *kwds)
{
static char* nams[] = {"first","second",NULL};
int first, second;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "ii", nams, &first, &second))
return -1;
((intpair*)self)->first = first;
((intpair*)self)->second = second;
return 0;
}
static void
intpair_dealloc(PyObject *self)
{
self->ob_type->tp_free(self);
}
static PyObject* nothing_method(PyObject* self, PyObject* args)
{
printf("This does literally nothing!\n");
Py_RETURN_NONE;
}
static PyMemberDef intpair_members[] = {
{"first", T_INT, offsetof(intpair, first), 0, "first item" },
{"second", T_INT, offsetof(intpair, second), 0, "second item" },
{NULL}
};
static PyTypeObject t_intpair = {
PyObject_HEAD_INIT(0) /* tp_head */
0, /* tp_internal */
"test.intpair", /* tp_name */
sizeof(intpair), /* tp_basicsize */
0, /* tp_itemsize */
intpair_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT,
"two ints (first,second)",
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
intpair_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
intpair_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
PyObject_Del, /* tp_free */
};
static PyMemberDef Noddy_members[] = {
{"PI", T_INT, 3, 0, "noddy number"},
{NULL, NULL, 0, NULL}
};
static PyMethodDef CosMethods[] =
{
{"does_nothing", nothing_method, METH_VARARGS, "This really does nothing"},
{NULL, NULL, 0, NULL}
};
static PyModuleDef testmodule = {
PyModuleDef_HEAD_INIT,
.m_name = "test",
.m_doc = "Example module that creates an extension type.",
.m_size = -1,
CosMethods,
};
PyMODINIT_FUNC
PyInit_test(void)
{
PyObject *m;
if (PyType_Ready(&t_intpair) < 0)
return NULL;
m = PyModule_Create(&testmodule);
if (m == NULL)
return NULL;
Py_INCREF(&t_intpair);
PyModule_AddObject(m, "Custom", (PyObject *) &t_intpair);
return m;
}
And the file setup.py:
from distutils.core import setup, Extension
setup(name="test", version="1.0",
ext_modules=[
Extension("test", ["test.c"]),
])
I can successfully compile this module using python3 setup.py build_ext --inplace, however, when I import this module in python 3 I get the following error:
Python 3.7.2 (default, Jan 10 2019, 23:51:51)
[GCC 8.2.1 20181127] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: Type does not define the tp_name field.
I do not understand this error message, since there is a tp_name field "test.intpair" defined in PyTypeObject. What am I missing here?
Also, what is necessary to change in this code to make it compile for python 3?
Thanks!

I'm pretty sure the flaw is the line 0, /* tp_internal */ in:
PyObject_HEAD_INIT(0) /* tp_head */
0, /* tp_internal */
"test.intpair", /* tp_name */
If you search for this line the only references to it on the internet are this question and the book you're using - it's completely non-standard and I have no idea where it's come from. I half suspect that the Python 2 code uses PyVarObject and then uses tp_internal to fill in the size rather than PyVarObject_HEAD_INIT, but I can't see the relevant page of the book...
The current documentation recommends using C99 initialization like
static PyTypeObject t_intpair = {
PyObject_HEAD_INIT(0)
.tp_name = "test.intpair",
// etc
};
so that getting the order of the attributes right no longer matters (anything you don't specify is 0 initialized).
Building your code provides some pretty clear warnings about mismatched types:
warning: initialization of ‘long int’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
"test_mod.intpair", /* tp_name */`
You should pay attention to these

Not a direct answer to your question, so posting this is probably against site rules. I hit this page when looking for advice about how to do a similar thing, converting a python2 - C extension module to python3 - C.
This is generally not a trivial endeavor, as the interface changed significantly. I think you will find the resources at this link helpful:
https://docs.python.org/3/howto/cporting.html

Related

Why does trace_printf("%f",x); trigger a hard fault on an STM32 MCU?

I'm using an STM32 F413ZH microcontroller with HAL libraries (mainly written to be used with C, but can also be used with C++) in Eclipse. Actually I managed to completely configure Eclipse including semihosting and debug mode (OpenOCD), the basic project files and also I managed to manually adapt the basic project files given by STM32CubeMX to work with C++. So... now I can use HAL libraries in a C++ Eclipse environment and test my code via OpenOCD and trace_printf trace_puts using semihosting.
Now, again, after struggling too much setting up an STM32 environment, I find myself stuck, but this time is different.
These last seven days I have been looking for a solution to my problem, and I have tried many suggestions from similar issues online, but none of them has solved my problem.
Well, I'm facing a hard fault when using trace_printf() in semihosting while debugging. If I use this function to print an integer (%d) via semihosting everything is okay, and I can read the printed value in the OpenOCD console, but when I tried to print a value with the %f formatter the supposedly printed data wasn't being shown in the OpenOCD console.
Then I read that in order to enable the printing of floating point values I needed to add -u _printf_float to the linker flags, so after adding the flag I tried to trace_printf() a floating value, an integer value, or whatever data type, but all of them using the %f formatter, but I keep getting a hard fault using %f in trace_printf().
[HardFault]
Stack frame:
R0 = 00666E69
R1 = 2004FE78
R2 = 2004FF00
R3 = 00666E69
R12 = F642D800
LR = 08005DE7
PC = 08006586
PSR = 01000000
FSR/FAR:
CFSR = 00008200
HFSR = 40000000
DFSR = 0000000A
AFSR = 00000000
BFAR = 00666E69
Misc
LR/EXC_RETURN= FFFFFFF9
By debugging step by step, the hard fault handler is triggered after this function is called: vsnprintf()
I'm using these linker flags:
-T mem.ld -T libs.ld -T sections.ld -Xlinker --gc-sections -L"../ldscripts" -Wl,-Map,"ThreePhaseSignals.map" --specs=nano.specs -u _printf_float
My project settings are:
Project toolchains
Target processor settings
C++ preprocessor
Linker settings and flags
My _sbrk.c is:
_sbrk.c
My linker files are:
mem.ld is:
mem.ld
And sections.ld is:
/*
* Default linker script for Cortex-M (it includes specifics for
STM32F[34]xx).
*
* To make use of the multi-region initialisations, define
* OS_INCLUDE_STARTUP_INIT_MULTIPLE_RAM_SECTIONS for the _startup.c file.
*/
/*
* The '__stack' definition is required by crt0, do not remove it.
*/
__stack = ORIGIN(RAM) + LENGTH(RAM);
_estack = __stack; /* STM specific definition */
/*
* Default stack sizes.
* These are used by the startup in order to allocate stacks
* for the different modes.
*/
__Main_Stack_Size = 1024;
PROVIDE ( _Main_Stack_Size = __Main_Stack_Size );
__Main_Stack_Limit = __stack - __Main_Stack_Size;
/* "PROVIDE" allows to easily override these values from an
* object file or the command line. */
PROVIDE(_Main_Stack_Limit = __Main_Stack_Limit);
/*
* There will be a link error if there is not this amount of
* RAM free at the end.
*/
_Minimum_Stack_Size = 256;
/*
* Default heap definitions.
* The heap start immediately after the last statically allocated
* .sbss/.noinit section, and extends up to the main stack limit.
*/
PROVIDE(_Heap_Begin = _end_noinit);
PROVIDE(_Heap_Limit = __stack - __Main_Stack_Size);
/*
* The entry point is informative, for debuggers and simulators,
* since the Cortex-M vector points to it anyway.
*/
ENTRY(_start)
/* Sections Definitions */
SECTIONS
{
/*
* For Cortex-M devices, the beginning of the startup code is stored in
* the .isr_vector section, which goes to FLASH.
*/
.isr_vector : ALIGN(4)
{
FILL(0xFF)
__vectors_start = ABSOLUTE(.);
__vectors_start__ = ABSOLUTE(.); /* STM specific definition */
KEEP(*(.isr_vector)) /* Interrupt vectors */
KEEP(*(.cfmconfig)) /* Freescale configuration words */
/*
* This section is here for convenience, to store the
* startup code at the beginning of the flash area, hoping that
* this will increase the readability of the listing.
*/
*(.after_vectors .after_vectors.*) /* Startup code and ISR */
} >FLASH
.inits : ALIGN(4)
{
/*
* Memory regions initialisation arrays.
*
* Thee are two kinds of arrays for each RAM region, one for
* data and one for bss. Each is iterrated at startup and the
* region initialisation is performed.
*
* The data array includes:
* - from (LOADADDR())
* - region_begin (ADDR())
* - region_end (ADDR()+SIZEOF())
*
* The bss array includes:
* - region_begin (ADDR())
* - region_end (ADDR()+SIZEOF())
*
* WARNING: It is mandatory that the regions are word aligned,
* since the initialisation code works only on words.
*/
__data_regions_array_start = .;
LONG(LOADADDR(.data));
LONG(ADDR(.data));
LONG(ADDR(.data)+SIZEOF(.data));
LONG(LOADADDR(.data_CCMRAM));
LONG(ADDR(.data_CCMRAM));
LONG(ADDR(.data_CCMRAM)+SIZEOF(.data_CCMRAM));
__data_regions_array_end = .;
__bss_regions_array_start = .;
LONG(ADDR(.bss));
LONG(ADDR(.bss)+SIZEOF(.bss));
LONG(ADDR(.bss_CCMRAM));
LONG(ADDR(.bss_CCMRAM)+SIZEOF(.bss_CCMRAM));
__bss_regions_array_end = .;
/* End of memory regions initialisation arrays. */
/*
* These are the old initialisation sections, intended to contain
* naked code, with the prologue/epilogue added by crti.o/crtn.o
* when linking with startup files. The standalone startup code
* currently does not run these, better use the init arrays below.
*/
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
/*
* The preinit code, i.e. an array of pointers to initialisation
* functions to be performed before constructors.
*/
PROVIDE_HIDDEN (__preinit_array_start = .);
/*
* Used to run the SystemInit() before anything else.
*/
KEEP(*(.preinit_array_sysinit .preinit_array_sysinit.*))
/*
* Used for other platform inits.
*/
KEEP(*(.preinit_array_platform .preinit_array_platform.*))
/*
* The application inits. If you need to enforce some order in
* execution, create new sections, as before.
*/
KEEP(*(.preinit_array .preinit_array.*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/*
* The init code, i.e. an array of pointers to static constructors.
*/
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/*
* The fini code, i.e. an array of pointers to static destructors.
*/
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/*
* For some STRx devices, the beginning of the startup code
* is stored in the .flashtext section, which goes to FLASH.
*/
.flashtext : ALIGN(4)
{
*(.flashtext .flashtext.*) /* Startup code */
} >FLASH
/*
* The program code is stored in the .text section,
* which goes to FLASH.
*/
.text : ALIGN(4)
{
*(.text .text.*) /* All remaining code */
/* Read-only data (constants) */
*(.rodata .rodata.* .constdata .constdata.*)
*(vtable) /* C++ virtual tables */
KEEP(*(.eh_frame*))
/*
* Stub sections generated by the linker, to glue together
* ARM and Thumb code. .glue_7 is used for ARM code calling
* Thumb code, and .glue_7t is used for Thumb code calling
* ARM code. Apparently always generated by the linker, for some
* architectures, so better leave them here.
*/
*(.glue_7)
*(.glue_7t)
} >FLASH
/* ARM magic sections */
.ARM.extab : ALIGN(4)
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
. = ALIGN(4);
__exidx_start = .;
.ARM.exidx : ALIGN(4)
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
. = ALIGN(4);
_etext = .;
__etext = .;
/* MEMORY_ARRAY */
/*
.ROarraySection :
{
*(.ROarraySection .ROarraySection.*)
} >MEMORY_ARRAY
*/
/*
* The secondary initialised data section.
*/
.data_CCMRAM : ALIGN(4)
{
FILL(0xFF)
*(.data.CCMRAM .data.CCMRAM.*)
. = ALIGN(4) ;
} > CCMRAM AT>FLASH
/*
* This address is used by the startup code to
* initialise the .data section.
*/
_sidata = LOADADDR(.data);
/*
* The initialised data section.
*
* The program executes knowing that the data is in the RAM
* but the loader puts the initial values in the FLASH (inidata).
* It is one task of the startup to copy the initial values from
* FLASH to RAM.
*/
.data : ALIGN(4)
{
FILL(0xFF)
/* This is used by the startup code to initialise the .data section */
_sdata = . ; /* STM specific definition */
__data_start__ = . ;
*(.data_begin .data_begin.*)
*(.data .data.*)
*(.data_end .data_end.*)
. = ALIGN(4);
/* This is used by the startup code to initialise the .data section */
_edata = . ; /* STM specific definition */
__data_end__ = . ;
} >RAM AT>FLASH
/*
* The uninitialised data sections. NOLOAD is used to avoid
* the "section `.bss' type changed to PROGBITS" warning
*/
/* The secondary uninitialised data section. */
.bss_CCMRAM (NOLOAD) : ALIGN(4)
{
*(.bss.CCMRAM .bss.CCMRAM.*)
} > CCMRAM
/* The primary uninitialised data section. */
.bss (NOLOAD) : ALIGN(4)
{
__bss_start__ = .; /* Standard newlib definition */
_sbss = .; /* STM specific definition */
*(.bss_begin .bss_begin.*)
*(.bss .bss.*)
*(COMMON)
*(.bss_end .bss_end.*)
. = ALIGN(4);
__bss_end__ = .; /* Standard newlib definition */
_ebss = . ; /* STM specific definition */
} >RAM
.noinit_CCMRAM (NOLOAD) : ALIGN(4)
{
*(.noinit.CCMRAM .noinit.CCMRAM.*)
} > CCMRAM
.noinit (NOLOAD) : ALIGN(4)
{
_noinit = .;
*(.noinit .noinit.*)
. = ALIGN(4) ;
_end_noinit = .;
} > RAM
/* Mandatory to be word aligned, _sbrk assumes this */
PROVIDE (end = _end_noinit); /* Was _ebss */
PROVIDE (_end = _end_noinit);
PROVIDE (__end = _end_noinit);
PROVIDE (__end__ = _end_noinit);
/*
* Used for validation only, do not allocate anything here!
*
* This is just to check that there is enough RAM left for the Main
* stack. It should generate an error if it's full.
*/
._check_stack : ALIGN(4)
{
. = . + _Minimum_Stack_Size;
} >RAM
/*
* The FLASH Bank1.
* The C or assembly source must explicitly place the code
* or data there using the "section" attribute.
*/
.b1text : ALIGN(4)
{
*(.b1text) /* Remaining code */
*(.b1rodata) /* Read-only data (constants) */
*(.b1rodata.*)
} >FLASHB1
/*
* The EXTMEM.
* The C or assembly source must explicitly place the code or data there
* using the "section" attribute.
*/
/* EXTMEM Bank0 */
.eb0text : ALIGN(4)
{
*(.eb0text) /* Remaining code */
*(.eb0rodata) /* Read-only data (constants) */
*(.eb0rodata.*)
} >EXTMEMB0
/* EXTMEM Bank1 */
.eb1text : ALIGN(4)
{
*(.eb1text) /* Remaining code */
*(.eb1rodata) /* Read-only data (constants) */
*(.eb1rodata.*)
} >EXTMEMB1
/* EXTMEM Bank2 */
.eb2text : ALIGN(4)
{
*(.eb2text) /* Remaining code */
*(.eb2rodata) /* read-only data (constants) */
*(.eb2rodata.*)
} >EXTMEMB2
/* EXTMEM Bank0 */
.eb3text : ALIGN(4)
{
*(.eb3text) /* Remaining code */
*(.eb3rodata) /* Read-only data (constants) */
*(.eb3rodata.*)
} >EXTMEMB3
/* After that there are only debugging sections. */
/* This can remove the debugging information from the standard libraries */
/*
DISCARD :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
*/
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/*
* DWARF debug sections.
* Symbols in the DWARF debugging sections are relative to the beginning
* of the section so we begin them at 0.
*/
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
}
On the Internet many people says that this is because printing floats consumes a lot of memory and probable the microcontroller is crashing due to memory overflow, so many suggestions aim to modify the linker script where stack and heap assignations are made, and some others say that this hard fault is related to_sbrk.c in newlib.
I tried to adapt these solutions to my particular case, but till now my problem still is not solved. I don't know if I'm badly implementing the suggestions or simply my problem is different.
How can I fix this problem?
Solved. Follow the following video and if it doesn't work follow the full series of videos.
STM32 with Eclipse, GNU ARM and J-Link. Part 4 - Minimal CMSIS Project - ITM Printf Debugging

CMSIS UART driver for Tiva launchpad

I'm trying to make the CMSIS UART driver example work on my Tiva launchpad: https://www.keil.com/pack/doc/CMSIS/Driver/html/group__usart__interface__gr.html
My code is the following:
#include "Driver_USART.h"
#include <cmsis_os.h> /* ARM::CMSIS:RTOS:Keil RTX */
#include <stdio.h>
#include <string.h>
/* USART Driver */
extern ARM_DRIVER_USART Driver_UART0;
void myUART_Thread(void const *argument);
osThreadId tid_myUART_Thread;
void myUSART_callback(uint32_t event)
{
switch (event)
{
case ARM_USART_EVENT_RECEIVE_COMPLETE:
case ARM_USART_EVENT_TRANSFER_COMPLETE:
case ARM_USART_EVENT_SEND_COMPLETE:
case ARM_USART_EVENT_TX_COMPLETE:
/* Success: Wakeup Thread */
osSignalSet(tid_myUART_Thread, 0x01);
break;
case ARM_USART_EVENT_RX_TIMEOUT:
__breakpoint(0); /* Error: Call debugger or replace with custom error handling */
break;
case ARM_USART_EVENT_RX_OVERFLOW:
case ARM_USART_EVENT_TX_UNDERFLOW:
__breakpoint(0); /* Error: Call debugger or replace with custom error handling */
break;
}
}
/* CMSIS-RTOS Thread - UART command thread */
void myUART_Thread(const void* args)
{ // static ARM_DRIVER_USART * USARTdrv = &Driver_UART0;
ARM_DRIVER_VERSION version;
ARM_USART_CAPABILITIES drv_capabilities;
char cmd;
/*Initialize the USART driver */
Driver_UART0.Initialize(myUSART_callback);
/*Power up the USART peripheral */
Driver_UART0.PowerControl(ARM_POWER_FULL);
/*Configure the USART to 4800 Bits/sec */
Driver_UART0.Control(ARM_USART_MODE_ASYNCHRONOUS |
ARM_USART_DATA_BITS_8 |
ARM_USART_PARITY_NONE |
ARM_USART_STOP_BITS_1 |
ARM_USART_FLOW_CONTROL_NONE, 4800);
/* Enable Receiver and Transmitter lines */
Driver_UART0.Control (ARM_USART_CONTROL_TX, 1);
Driver_UART0.Control (ARM_USART_CONTROL_RX, 1);
Driver_UART0.Send("\nPress Enter to receive a message", 34);
osSignalWait(0x01, osWaitForever);
while (1)
{
Driver_UART0.Receive(&cmd, 1); /* Get byte from UART */
osSignalWait(0x01, osWaitForever);
if (cmd == 13) /* CR, send greeting */
{
Driver_UART0.Send("\nHello World!", 12);
osSignalWait(0x01, osWaitForever);
}
}
}
int main(void){
osKernelInitialize (); // initialize CMSIS-RTOS
osKernelStart (); // start thread execution
}
Now I don't expect it to work right away, however my builder starts complaining at the linkig phase that Driver_UART0 is undefined:
.\Objects\bl.axf: Error: L6218E: Undefined symbol Driver_UART0 (referred from blinky.o).
Any idea what I could do to fix this?
Thank you,
Botond
Unfortunately TI doesn't support the CMSIS Driver API at this time. I've have been working on creating this API on my own and have a working example in my GitHub repository:
This is the example:
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/blob/master/CMSIS-Driver-Examples/UART-CMSIS-Driver/main.c
You'll have to set your include path in project options to this folder:
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/tree/master/inc
Also add the files in this folder to your project:
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/tree/master/src
and also this file
https://github.com/AllAboutEE/ARM-Programming-Examples-TM4C/blob/master/startup_LM4F.s

How to add a built-in module to a C-Python API after Py_Initialize?

I have a module defined in my C code like so:
static struct PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
"the_module_name", /* m_name */
module_documentation, /* m_doc */
//....
};
and a function to initialize it:
PyMODINIT_FUNC init_the_module(void)
{
PyObject *mod, *submodule;
PyObject *sys_modules = PyThreadState_GET()->interp->modules;
mod = PyModule_Create(&module_def);
PyModule_AddObject(mod, "some_submodule", (submodule = init_the_submodule()));
PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule);
Py_INCREF(submodule);
// more submodules..
return mod;
}
The application that I am embedding python into is quite big and I can not change the workflow much. At this point Py_Initialize has already been called, so I can not call PyImport_ExtendInittabor PyImport_AppendInittab .
How can I create and add the module to the system modules?
Maybe I can manipulate the modules dictionary directly? Like so:
PyObject *modules, *the_module;
modules = PyImport_GetModuleDict();
PyDict_SetItemString(modules, "the_module_name", init_the_module());
the_module = PyDict_GetItemString(modules, "the_module_name"); //this is getting something back
std::cout << PyDict_Size(the_module) << std::endl; // this prints -1
The easiest way to handle this is to statically initialize your statically-linked modules by directly calling initspam() after the call to Py_Initialize() or PyMac_Initialize():
int main(int argc, char **argv)
{
/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(argv[0]);
/* Initialize the Python interpreter. Required. */
Py_Initialize();
/* Add a static module */
initspam();
An example may be found in the file Demo/embed/demo.c in the Python source distribution.

Undeclared identifiers (C-mex S-Functions)

I understand that this is a very common problem that a lot is facing. however I don't understand how to link to my program. It's a simple c = a+b program. I have the following as my C-Mex S-function:
#define S_FUNCTION_NAME Addition
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
static void mdlInitializeSizes(SimStruct *S)
{
int_T nInputPorts = 2; /* Set no. of input ports */
int_T nOutputPorts = 1; /* Set no. of output ports */
int_T needsInput = 1; /* Direct feed through = yes */
int_T inputPortIdx = 0;
int_T outputPortIdx = 0;
ssSetNumSFcnParams(S, 0);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S))
{
return; /* If no. of expected input parameters is not equal to no.
of parameters entered in dialog box, return. Simulink
to generate an error indicating parameter mismatched */
}
/* Configure input ports */
if (!ssSetNumInputPorts(S, 2)) return;
/* Configure first input ports */
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED); /* Set dimensions of first input port */
ssSetInputPortDirectFeedThrough(S, 0, 1); /* Set direct feed through */
/* Configure second input ports */
ssSetInputPortWidth(S, 1, DYNAMICALLY_SIZED); /* Set dimensions of second input port */
ssSetInputPortDirectFeedThrough(S, 1, 1); /* Set direct feed through */
/* Configure output ports */
if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED); /* Set dimensions of output ports */
ssSetNumSampleTimes(S, 1); /* Set no. of sample times */
ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE);
ssSetOptions (S,0);
} /* End of mdlInitializeSizes */
static void mdlInitializeSampleTimes (SimStruc *S)
{
ssSetSampleTimes(S, 0, INHERITED_SAMPLE_TIME);
/* Inherited Sample time: S-function block executes whenever driving block executes */
ssSetOffsetTime(S, 0, 0.0); /* No offset required */
ssSetModelReferenceSampleTimeDefaultInheritance(S); /* */
} /* End of mdlInitializeSampleTime */
static void mdlOutputs (SimStruc *S, int_T tid)
{
int_T i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T width = ssGetOutputPortWidth(S,0);
for (i=0; i<width; i++) /* i = location of memory. from 0 to 1. */
{
*y++ = (*uPtrs[i+1]) + (*uPtrs[i]); /* c = a + b */
}
} /* End of mdlOutputs */
static void mdlTerminate(SimStruct *S)
{
/* No task to be perform at end of simulation therefore no termination required.
But this is a compulsory function to have for C-mex S-function */
} /* End of mdlTerminate */
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif
however when compiling on matlab, i keep getting the following error:
Addition.c:47: error: expected ‘)’ before ‘*’ token
Addition.c:56: error: expected ‘)’ before ‘*’ token
In file included from Addition.c:82:
/DIST/it/sw/amd64/matlab/r2010b/simulink/include/simulink.c: In function ‘_ProcessMexSfunctionCmdLineCall’:
/DIST/it/sw/amd64/matlab/r2010b/simulink/include/simulink.c:2545: error: ‘mdlInitializeSampleTimes’ undeclared (first use in this function)
/DIST/it/sw/amd64/matlab/r2010b/simulink/include/simulink.c:2545: error: (Each undeclared identifier is reported only once
/DIST/it/sw/amd64/matlab/r2010b/simulink/include/simulink.c:2545: error: for each function it appears in.)
/DIST/it/sw/amd64/matlab/r2010b/simulink/include/simulink.c:2601: error: ‘mdlOutputs’ undeclared (first use in this function)
Any comments will be greatly appreciated!
You misspelled SimStruct in the two lines mentioned at the top of your error message. That error about the parentheses means that the compiler doesn't know that SimStruc is a type, so the * doesn't fit. After that, those two functions aren't defined, so the simulink interface gets upset that it can't find those two required functions in your file.

Type error switching from .m to .mm

I encountered this error:
cannot convert 'V_DTLPROMO_BTN_FN_T' to 'V_DTLPROD_BTN_FN_T' in argument passing
in code line:
[ m_vPromoDetails changeButton : V_DTLPROMO_BTN_2 btnFn : V_DTLPROMO_BTN_FN_ADD ];
Both arguments are of type V_DTLPROMO_BTN_T & V_DTLPROMO_BTN_FN_T which is are enum declared in the .h file. Things were working fine until I changed the implementation file extension to .mm from .m to accommodate some C/C++ related code.
Prototype declaration
- ( void )changeButton : ( V_DTLPROMO_BTN_T )button
btnFn : ( V_DTLPROMO_BTN_FN_T )btnFn
Enum definitions
typedef enum
{
V_DTLPROMO_BTN_FN_NONE = 0, /**< None */
V_DTLPROMO_BTN_FN_RECOMMEND = 1, /**< Recommend */
V_DTLPROMO_BTN_FN_ADD = 2, /**< Add */
V_DTLPROMO_BTN_FN_DELETE = 3, /**< Delete */
V_DTLPROMO_BTN_FN_LOCATE = 4, /**< Locate */
V_DTLPROMO_BTN_FN_BUY_NOW = 5, /**< Buy Now */
V_DTLPROMO_BTN_FN_SHOPPED = 6, /**< Shopped */
V_DTLPROMO_BTN_FN_TWEET = 7, /**< Locate */
V_DTLPROMO_BTN_FN_NUM = 8 /**< Number of function choices */
} V_DTLPROMO_BTN_FN_T;
typedef enum
{
V_DTLPROMO_BTN_1 = 0, /**< Button: 1 */
V_DTLPROMO_BTN_2 = 1, /**< Button: 2 */
V_DTLPROMO_BTN_3 = 2, /**< Button: 3 */
V_DTLPROMO_BTN_NUM = 3 /**< Number of buttons */
} V_DTLPROMO_BTN_T;
V_DTLPROD_BTN_FN_T is an enum declared in another class.My detailpromo class was somehow getting confused by that enum in detailprod class.I noticed that the function names were same in both the classes ,so I tried to change the function name from changeButton to changeButtonForPromo.That did the trick and errors were removed.Still havent got the technical explanation for that.