I wish to generate arrays in a C extension module and pass them back to python.
The following code works for python2:
C_generate_array.c:
#include "Python.h"
#include "arrayobject.h"
#include "C_generate_array.h"
#include <assert.h>
static PyMethodDef C_generate_arrayMethods[] = {
{"get_array", get_array, METH_VARARGS},
{NULL, NULL} /* Sentinel - marks the end of this structure */
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"C_generate_array", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
C_generate_arrayMethods
};
PyMODINIT_FUNC PyInit_C_generate_array(void)
{
return PyModule_Create(&cModPyDem);
}
#else
void initC_generate_array() {
(void) Py_InitModule("C_generate_array", C_generate_arrayMethods);
import_array(); // Must be present for NumPy. Called first after above line.
}
#endif
static PyObject *get_array(PyObject *self, PyObject *args)
{
int dims[2];
dims[0]=dims[1]=2;
PyArrayObject *matout;
#if PY_MAJOR_VERSION >= 3
//what to do here?
return PyLong_FromLong(1);
#else
matout = (PyArrayObject *) PyArray_FromDims(2,dims,NPY_DOUBLE);
return PyArray_Return(matout);
#endif
}
C_generate_array.h:
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif
typedef int bool;
#define true 1
#define false 0
static PyObject *get_array(PyObject *self, PyObject *args);
C_generate_array_setup.py:
from distutils.core import setup, Extension
module1 = Extension('C_generate_array',
include_dirs = ['path_to_python/lib/python3.5/','path_to_python/lib/python3.5/site-packages/numpy/core/include/numpy/'],
sources = ['C_generate_array.c'])
setup (name = 'C_generate_array',
version = '1.0',
description = 'Example',
ext_modules = [module1])
Then building and installing:
>sudo python2.7 C_generate_array_setup.py build
>sudo python2.7 C_generate_array_setup.py install
>python2.7
>>> import C_generate_array
>>> C_generate_array.get_array()
array([[0., 0.],
[0., 0.]])
However, what is the equivalent of this for python3? I only found a way to return scalar variables:
>sudo python3.5 C_generate_array_setup.py build
>sudo python3.5 C_generate_array_setup.py install
>python3.5
>>> import C_generate_array
>>> C_generate_array.get_array()
1
How can I return arrays?
I think the issue is that PyArray_FromDims is a very old API function that is no longer recommended, and may have been removed from the Numpy headers. I don't know why it seems to work for Python 2, but it's possible that you have an older version of Numpy installed there.
I suggest you use PyArray_ZEROS instead, which has basically the same interface with an additional parameter to mark if the array should be Fortran contiguous (you probably want to set this to 0). If you want to fill the array to something other than zero then pick a different function (read the documentation I have linked).
Related
I have a simple C-extension(see example below) that sometimes prints using the printf function.
I'm looking for a way to wrap the calls to the function from that C-extensions so that all those printfs will be redirected to my python logger.
hello.c:
#include <Python.h>
static PyObject* hello(PyObject* self)
{
printf("example print from a C code\n");
return Py_BuildValue("");
}
static char helloworld_docs[] =
"helloworld(): Any message you want to put here!!\n";
static PyMethodDef helloworld_funcs[] = {
{"hello", (PyCFunction)hello,
METH_NOARGS, helloworld_docs},
{NULL}
};
static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"helloworld",
"Extension module example!",
-1,
helloworld_funcs
};
PyMODINIT_FUNC PyInit_helloworld(void)
{
return PyModule_Create(&cModPyDem);
};
setup.py:
from distutils.core import setup, Extension
setup(name = 'helloworld', version = '1.0', \
ext_modules = [Extension('helloworld', ['hello.c'])])
to use first run
python3 setup.py install
and then:
import helloworld
helloworld.hello()
I want to be able to do something like this:
with redirect_to_logger(my_logger)
helloworld.hello()
EDIT: I saw a number of posts showing how to silence the prints from C, but I wasn't able to figure out from it how can I capture the prints in python instead.
Example of such post: Redirect stdout from python for C calls
I assume that this question didn't get much traction because I maybe ask too much, so I don't care about logging anymore... how can I capture the C prints in python? to a list or whatever.
EDIT
So I was able to achieve somewhat a working code that does what I want - redirect c printf to python logger:
import select
import threading
import time
import logging
import re
from contextlib import contextmanager
from wurlitzer import pipes
from helloworld import hello
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
class CPrintsHandler(threading.Thread):
def __init__(self, std, poll_std, err, poll_err, logger):
super(CPrintsHandler, self).__init__()
self.std = std
self.poll_std = poll_std
self.err = err
self.poll_err = poll_err
self.logger = logger
self.stop_event = threading.Event()
def stop(self):
self.stop_event.set()
def run(self):
while not self.stop_event.is_set():
# How can I poll both std and err at the same time?
if self.poll_std.poll(1):
line = self.std.readline()
if line:
self.logger.debug(line.strip())
if self.poll_err.poll(1):
line = self.err.readline()
if line:
self.logger.debug(line.strip())
#contextmanager
def redirect_to_logger(some_logger):
handler = None
try:
with pipes() as (std, err):
poll_std = select.poll()
poll_std.register(std, select.POLLIN)
poll_err = select.poll()
poll_err.register(err, select.POLLIN)
handler = CPrintsHandler(std, poll_std, err, poll_err, some_logger)
handler.start()
yield
finally:
if handler:
time.sleep(0.1) # why do I have to sleep here for the foo prints to finish?
handler.stop()
handler.join()
def foo():
logger.debug('logger print from foo()')
hello()
def main():
with redirect_to_logger(logger):
# I don't want the logs from here to be redirected as well, only printf.
logger.debug('logger print from main()')
foo()
main()
But I have a couple of issues:
The python logs are also being redirected and caught by the CPrintsHandler. Is there a way to avoid that?
The prints are not exactly in the correct order:
python3 redirect_c_example_for_stackoverflow.py
2020-08-18 19:50:47,732 - root - DEBUG - example print from a C code
2020-08-18 19:50:47,733 - root - DEBUG - 2020-08-18 19:50:47,731 - root - DEBUG - logger print from main()
2020-08-18 19:50:47,733 - root - DEBUG - 2020-08-18 19:50:47,731 - root - DEBUG - logger print from foo()
Also, the logger prints all go to err, perhaps the way I poll them causes this order.
I'm not that familiar with select in python and not sure if there is a way to poll both std and err at the same time and print whichever has something first.
On Linux you could use wurlitzer which would capture the output from fprint, e.g.:
from wurlitzer import pipes
with pipes() as (out, err):
helloworld.hello()
out.read()
#'example print from a C code\n'
wurlitzer is based on this article of Eli Bendersky, the code from which you can use if you don't like to depend on third-party libraries.
Sadly, wurlitzer and the code from the article work only for Linux (and possible MacOS).
Here is a prototype (an improved version of the prototype can be installed from my github) for Windows using Eli's approach as Cython-extension (which probably could be translated to ctypes if needed):
%%cython
import io
import os
cdef extern from *:
"""
#include <windows.h>
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
int open_temp_file() {
TCHAR lpTempPathBuffer[MAX_PATH+1];//path+NULL
// Gets the temp path env string (no guarantee it's a valid path).
DWORD dwRetVal = GetTempPath(MAX_PATH, // length of the buffer
lpTempPathBuffer); // buffer for path
if(dwRetVal > MAX_PATH || (dwRetVal == 0))
{
return -1;
}
// Generates a temporary file name.
TCHAR szTempFileName[MAX_PATH + 1];//path+NULL
DWORD uRetVal = GetTempFileName(lpTempPathBuffer, // directory for tmp files
TEXT("tmp"), // temp file name prefix
0, // create unique name
szTempFileName); // buffer for name
if (uRetVal == 0)
{
return -1;
}
HANDLE tFile = CreateFile((LPTSTR)szTempFileName, // file name
GENERIC_READ | GENERIC_WRITE, // first we write than we read
0, // do not share
NULL, // default security
CREATE_ALWAYS, // overwrite existing
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, // "temporary" temporary file, see https://learn.microsoft.com/en-us/archive/blogs/larryosterman/its-only-temporary
NULL); // no template
if (tFile == INVALID_HANDLE_VALUE) {
return -1;
}
return _open_osfhandle((intptr_t)tFile, _O_APPEND | _O_TEXT);
}
int replace_stdout(int temp_fileno)
{
fflush(stdout);
int old;
int cstdout = _fileno(stdout);
old = _dup(cstdout); // "old" now refers to "stdout"
if (old == -1)
{
return -1;
}
if (-1 == _dup2(temp_fileno, cstdout))
{
return -1;
}
return old;
}
int restore_stdout(int old_stdout){
fflush(stdout);
// Restore original stdout
int cstdout = _fileno(stdout);
return _dup2(old_stdout, cstdout);
}
void rewind_fd(int fd) {
_lseek(fd, 0L, SEEK_SET);
}
"""
int open_temp_file()
int replace_stdout(int temp_fileno)
int restore_stdout(int old_stdout)
void rewind_fd(int fd)
void close_fd "_close" (int fd)
cdef class CStdOutCapture():
cdef int tmpfile_fd
cdef int old_stdout_fd
def start(self): #start capturing
self.tmpfile_fd = open_temp_file()
self.old_stdout_fd = replace_stdout(self.tmpfile_fd)
def stop(self): # stops capturing, frees resources and returns the content
restore_stdout(self.old_stdout_fd)
rewind_fd(self.tmpfile_fd) # need to read from the beginning
buffer = io.TextIOWrapper(os.fdopen(self.tmpfile_fd, 'rb'))
result = buffer.read()
close_fd(self.tmpfile_fd)
return result
And now:
b = CStdOutCapture()
b.start()
helloworld.hello()
out = b.stop()
print("HERE WE GO:", out)
# HERE WE GO: example print from a C code
This is what I would do if I am free to edit the C code. Open a memory map in C and write to its file descriptor using fprintf(). Expose the file descriptor to Python either as the int and then use mmap module to open it or use os.openfd() to wrap it in a simpler file-like object, or wrap it in file-like object in C and let Python use that.
Then I would create a class that will enable me to write to sys.stdout through usual interface, i.e. its write() method (for Python's side usage) , and that would use select module to poll the file from C that acts as its stdout in a thread. Then I would switch sys.stdout with an object of this class. So, when Python does sys.stdout.write(...) the string will be redirected to sys.stdout.write(), and when the loop in a thread detects output on a file from C, it will write it using sys.stdout.write(). So, everything will be written to the screen and be available to loggers as well.
In this model, the strictly C part will never actually be writing to the file descriptor connected to the terminal.
You can even do much of this in C itself and leave little for the Python's side, but its easier to influence the interpreter from the Python's side as the extension is the shared library which involves some kind of, lets call it, IPC and OS in the whole story. That's why the stdout is not shared between extension and Python in the first place.
If you want to continue printf() on C side, you can see how you can redirect it in C before programming this whole mess.
This answer is strictly theoretical because I have no time to test it; but it should be doable according to my knowledge. If you try it, please let me know in a comment how it went. Perhaps I missed something, but, I am certain the theory is sane.
Beauty of this idea is that it will be OS independent, although the part with shared memory or connecting a file descriptor to allocated space in RAM can be sometimes PITA on Windows.
If you are not constrained to using the printf in C, it would be easier to use the print equivalent from python C API and pass where you want to redirect the message as an argument.
For example, your hello.c would be:
#include <Python.h>
static PyObject* hello(PyObject* self, PyObject *args)
{
PyObject *file = NULL;
if (!PyArg_ParseTuple(args, "O", &file))
return NULL;
PyObject *pystr = PyUnicode_FromString("example print from a C code\n");
PyFile_WriteObject(pystr, file, Py_PRINT_RAW);
return Py_BuildValue("");
}
static char helloworld_docs[] =
"helloworld(): Any message you want to put here!!\n";
static PyMethodDef helloworld_funcs[] = {
{"hello", (PyCFunction)hello,
METH_VARARGS, helloworld_docs},
{NULL}
};
static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"helloworld",
"Extension module example!",
-1,
helloworld_funcs
};
PyMODINIT_FUNC PyInit_helloworld(void)
{
return PyModule_Create(&cModPyDem);
};
We can check if it is working with the program below:
import sys
import helloworld
helloworld.hello(sys.stdout)
helloworld.hello(sys.stdout)
helloworld.hello(sys.stderr)
In the command line we redirect each output separately:
python3 example.py 1> out.txt 2> err.txt
out.txt will have two print calls, while err.txt will have only one, as expected from our python script.
You can check python's print implementation to get some more ideas of what you can do.
cpython print source code
I am new to Cython (as well as Python), and I am trying to understand where I am doing wrong when I try to expose the C-API of an external multithreaded library to Python. To illustrate my problem, I will go over a hypothetical MWE.
Let's assume that I have the following directory structure
.
├── app.py
├── c_mylib.pxd
├── cxx
│ ├── CMakeLists.txt
│ ├── include
│ │ └── mylib.h
│ └── src
│ └── reduce_cb.cpp
├── mylib.pyx
└── setup.py
Here, cxx contains the external multithreaded library as follows (header and implementation files are concatenated):
/* cxx/include/mylib.h */
#ifndef MYLIB_H_
#define MYLIB_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef double (*func_t)(const double *, const double *, void *);
double reduce_cb(const double *, const double *, func_t, void *);
#ifdef __cplusplus
}
#endif
#endif
/* cxx/src/reduce_cb.cpp */
#include <iterator>
#include <mutex>
#include <thread>
#include <vector>
#include "mylib.h"
extern "C" {
double reduce_cb(const double *xb, const double *xe, func_t func, void *data) {
const auto d = std::distance(xb, xe);
const auto W = std::thread::hardware_concurrency();
const auto split = d / W;
const auto remain = d % W;
std::vector<std::thread> workers(W);
double res{0};
std::mutex lock;
const double *xb_w{xb};
const double *xe_w;
for (unsigned int widx = 0; widx < W; widx++) {
xe_w = widx < remain ? xb_w + split + 1 : xb_w + split;
workers[widx] = std::thread(
[&lock, &res, func, data](const double *xb, const double *xe) {
const double partial = func(xb, xe, data);
std::lock_guard<std::mutex> guard(lock);
res += partial;
},
xb_w, xe_w);
xb_w = xe_w;
}
for (auto &worker : workers)
worker.join();
return res;
}
}
with the accompanying cxx/CMakeLists.txt file as follows:
cmake_minimum_required(VERSION 3.9)
project(dummy LANGUAGES CXX)
add_library(mylib
include/mylib.h
src/reduce_cb.cpp
)
target_compile_features(mylib
PRIVATE
cxx_std_11
)
target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
set_target_properties(mylib
PROPERTIES PUBLIC_HEADER include/mylib.h
)
install(TARGETS mylib
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
)
The corresponding Cython files are as follows (this time definition and implementation files are concatenated):
# c_mylib.pxd
cdef extern from "include/mylib.h":
ctypedef double (*func_t)(const double *, const double *, void *)
double reduce_cb(const double *, const double *, func_t, void *)
# mylib.pyx
# cython: language_level = 3
cimport c_mylib
cdef double func(const double *xb, const double *xe, void *data):
cdef int d = (xe - xb)
func = <object>data
return func(<double[:d]>xb)
def reduce_cb(double [::1] arr not None, f):
cdef int d = arr.shape[0]
data = <void*>f
return c_mylib.reduce_cb(&arr[0], &arr[0] + d, func, data)
# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
setup(
ext_modules=cythonize([
Extension("mylib", ["mylib.pyx"], libraries=["mylib"])
])
)
Building the C++ library, and building the Cython extension module and linking it against the C++ library following the instructions, I get undefined behavior when I try to run
import mylib
from numpy import array
def cb(x):
partial = 0
for idx in range(x.shape[0]):
partial += x[idx]
return partial
arr = array([val + 1 for val in range(100)], "d")
print("sum(arr): ", mylib.reduce_cb(arr, cb))
By undefined behavior, I mean that I get either of
SIGSEGV (Address boundary error),
"Fatal Python error: GC object already tracked" with SIGABRT, or,
(rarely) the correct result.
I have checked the documentation of Cython thoroughly (I guess), and I have searched both SO and Google for this problem, but I could not find a proper solution to this problem.
Basically, I would like to have a C library, which is unaware of Python and which uses callback functions from multiple threads, that is integrated inside Python. Is this at all possible? I tried nogil signatures and with gil: blocks as discussed in Cython's documentation, but I got compilation errors. Moreover, gc related functionality in Cython seems to be valid only for extension types, which does not apply to my case.
I am stuck and I would appreciate any hint/help.
That happens when you use Python-objects/functionality without lock. Your critical section is not only the summation but also the call to function func, i.e.:
workers[widx] = std::thread(
[&lock, &res, func, data](const double *xb, const double *xe) {
std::lock_guard<std::mutex> guard(lock);
const double partial = func(xb, xe, data); // must be guarded
res += partial;
},
xb_w, xe_w);
which makes the parallelization senseless in the first place, doesn't it? Probably, from software-engineering point of view, a better place for guard would be in the wrapper function func - but I have put it into worker because the consequences are seen much better this way.
Python uses reference counting for memory management - similar to std::shared_ptr. However it doesn't lock with fine granularity as shared_ptr, which locks only when changing the reference counter, but uses a more coarse lock - the global interpreter lock. That has the consequence, that when one changes the reference count of an python-object from open-MP-thread or other threads not registered in Python-interpreter the reference-counter is not protected/guarded and race conditions arise. What you are observing, are the possible results of such race conditions.
The GIL makes your endeavor more or less impossible: you need to lock every call to possible python but than you serialize the calls to this functionality!
I am trying to pass command line parameters using following code
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
static int nilvar=0;
static int nilvar2=0;
int rollcalls[5];// = {0};
char classname[10];// = "math";
module_param_named (var,nilvar2,int,0644);
module_param (nilvar,int,0644);
module_param_array_named(present,rollcalls,int,5,0644);
module_param_string(subject,classname,10,0644);
int init_module(void)
{
printk(KERN_INFO"1) nilvar = %d\n 2) nilvar2 = %d",nilvar,nilvar2);
printk(KERN_INFO/*NOTICE*/"ROLLCALLS = %d ,%d ,%d ,%d",rollcalls[0],rollcalls[1],rollcalls[2],rollcalls[3]);
printk(KERN_INFO/*DEBUG*/"classname = %s",classname);
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Bye....\n");
}
MODULE_LICENSE("GPL");
after make ,I am passing my arguments by
insmod module1.ko var=5 nilvar=6 present=1 2 3 4 subject=physics
I don't know exactly what is happening but now lsmod shows module used by -2.
(actually no module is dependent on this module)
so where I am wrong ? and if we want to modify all this variables as a structure elements, then how to use module_param() macro for it?
#user3452214, instead of module_param_array_named(present, rollcalls, int, **5**, 0644); use module_param_array_named(present, rollcalls, int, **&count**, 0644); added one more variable i.e. static unsigned int count which keep count of the number written to the array. We need to pass the pointer as explained in the moduleparam.h, thus cannot pass numerical value for this parameter. It works fine!!!. Hope it solves your problem.
/**
* module_param_array_named - renamed parameter which is an array of some type
* #name: a valid C identifier which is the parameter name
* #array: the name of the array variable
* #type: the type, as per module_param()
* #nump: optional pointer filled in with the number written
* #perm: visibility in sysfs
*
* This exposes a different name than the actual variable name. See
* module_param_named() for why this might be necessary.
*/
#define module_param_array_named(name, array, type, nump, perm)
I have a GNU/Linux application with uses a number of shared memory objects. It could, potentially, be run a number of times on the same system. To keep things tidy, I first create a directory in /dev/shm for each of the set of shared memory objects.
The problem is that on newer GNU/Linux distributions, I no longer seem to be able create these in a sub-directory of /dev/shm.
The following is a minimal C program with illustrates what I'm talking about:
/*****************************************************************************
* shm_minimal.c
*
* Test shm_open()
*
* Expect to create shared memory file in:
* /dev/shm/
* └── my_dir
* └── shm_name
*
* NOTE: Only visible on filesystem during execution. I try to be nice, and
* clean up after myself.
*
* Compile with:
* $ gcc -lrt shm_minimal.c -o shm_minimal
*
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, const char* argv[]) {
int shm_fd = -1;
char* shm_dir = "/dev/shm/my_dir";
char* shm_file = "/my_dir/shm_name"; /* does NOT work */
//char* shm_file = "/my_dir_shm_name"; /* works */
// Create directory in /dev/shm
mkdir(shm_dir, 0777);
// make shared memory segment
shm_fd = shm_open(shm_file, O_RDWR | O_CREAT, 0600);
if (-1 == shm_fd) {
switch (errno) {
case EINVAL:
/* Confirmed on:
* kernel v3.14, GNU libc v2.19 (ArchLinux)
* kernel v3.13, GNU libc v2.19 (Ubuntu 14.04 Beta 2)
*/
perror("FAIL - EINVAL");
return 1;
default:
printf("Some other problem not being tested\n");
return 2;
}
} else {
/* Confirmed on:
* kernel v3.8, GNU libc v2.17 (Mint 15)
* kernel v3.2, GNU libc v2.15 (Xubuntu 12.04 LTS)
* kernel v3.1, GNU libc v2.13 (Debian 6.0)
* kernel v2.6.32, GNU libc v2.12 (RHEL 6.4)
*/
printf("Success !!!\n");
}
// clean up
close(shm_fd);
shm_unlink(shm_file);
rmdir(shm_dir);
return 0;
}
/* vi: set ts=2 sw=2 ai expandtab:
*/
When I run this program on a fairly new distribution, the call to shm_open() returns -1, and errno is set to EINVAL. However, when I run on something a little older, it creates the shared memory object in /dev/shm/my_dir as expected.
For the larger application, the solution is simple. I can use a common prefix instead of a directory.
If you could help enlighten me to this apparent change in behavior it would be very helpful. I suspect someone else out there might be trying to do something similar.
So it turns out the issue stems from how GNU libc validates the shared memory name. Specifically, the shared memory object MUST now be at the root of the shmfs mount point.
This was changed in glibc git commit b20de2c3d9 as the result of bug BZ #16274.
Specifically, the change is the line:
if (name[0] == '\0' || namelen > NAME_MAX || strchr (name, '/') != NULL)
Which now disallows '/' from anywhere in the filename (not counting leading '/')
If you have a third party tool that was broken by this shm_open change, a brilliant coworker found a workaround : preload a library that overrides the shm_open call and swaps slashes for underscores. It does the same for shm_unlink as well, so the application can properly free shared memory when needed.
deslash_shm.cc :
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <algorithm>
#include <string>
// function used in place of the standard shm_open() function
extern "C" int shm_open(const char *name, int oflag, mode_t mode)
{
// keep a function pointer to the real shm_open() function
static int (*real_open)(const char *, int, mode_t) = NULL;
// the first time in, ask the dynamic linker to find the real shm_open() function
if (!real_open) real_open = (int (*)(const char *, int, mode_t)) dlsym(RTLD_NEXT,"shm_open");
// take the name we were given and replace all slashes with underscores instead
std::string n = name;
std::replace(n.begin(), n.end(), '/', '_');
// call the real open function with the patched path name
return real_open(n.c_str(), oflag, mode);
}
// function used in place of the standard shm_unlink() function
extern "C" int shm_unlink(const char *name)
{
// keep a function pointer to the real shm_unlink() function
static int (*real_unlink)(const char *) = NULL;
// the first time in, ask the dynamic linker to find the real shm_unlink() function
if (!real_unlink) real_unlink = (int (*)(const char *)) dlsym(RTLD_NEXT, "shm_unlink");
// take the name we were given and replace all slashes with underscores instead
std::string n = name;
std::replace(n.begin(), n.end(), '/', '_');
// call the real unlink function with the patched path name
return real_unlink(n.c_str());
}
To compile this file:
c++ -fPIC -shared -o deslash_shm.so deslash_shm.cc -ldl
And preload it before starting a process that tries to use non-standard slash characters in shm_open:
in bash:
export LD_PRELOAD=/path/to/deslash_shm.so
in tcsh:
setenv LD_PRELOAD /path/to/deslash_shm.so
I'm working on a Word 2011 plugin in Mac OS. Currently, I need to write a code in VBA Macro to retrieve a String from another application (through Socket communication). So, basically in Windows, I can simply make a DLL which help me to do Socket communication with the other application and return the String value to VBA Macro.
However, in Mac, I'm able to build a .dylib (in C) and using VBA to communicate with the dylib. However, I'm having a trouble with the return String. My simple C code is something like:
char * tcpconnect(char* arguments)
{}
First, it always contains Chr(0) characters. Secondly, I suspected that this C function will not be able to handle Unicode String.
Do you guys have any experiences or have any similar example of this?
Thanks,
David
My original post was an attempt to imitate SysAllocStringByteLen() using malloc(), but this will fail when Excel tries to free the returned memory. Using Excel to allocate the memory fixes that issue, and is less code as well, e.g.:
in test.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LPCSTR const char *
#define LPSTR char *
#define __declspec(dllexport)
#define WINAPI
char *saved_string = NULL;
int32_t saved_len = -1;
#define _CLEANUP if(saved_string) free(saved_string)
__attribute__((destructor))
static void finalizer(void) {
_CLEANUP;
}
int32_t __declspec(dllexport) WINAPI get_saved_string(LPSTR pszString, int cSize) {
int32_t old_saved_len = saved_len;
if(saved_len > 0 && cSize >= saved_len)
strncpy(pszString, saved_string, saved_len);
if(saved_string) {
free(saved_string);
saved_string = NULL;
saved_len = -1;
}
return old_saved_len;
}
int32_t __declspec(dllexport) WINAPI myfunc(LPCSTR *pszString) {
int len = (pszString && *pszString ? strlen(*pszString) : 0);
saved_string = malloc(len + 5);
saved_len = len + 5;
sprintf(saved_string, "%s%.*s", "abc:", len, *pszString);
return saved_len;
}
Compile the above with
gcc -g -arch i386 -shared -o test.dylib test.c
Then, in a new VBA module, use the below and run "test", which will prepend "abc:" to the string "hi there" and output the result the debug window:
Public Declare Function myfunc Lib "<colon-separated-path>:test.dylib" (s As String) As Long
Public Declare Function get_saved_string Lib "<colon-separated-path>:test.dylib" (ByVal s As String, ByVal csize As Long) As Long
Option Explicit
Public Function getDLLString(string_size As Long) As String
Dim s As String
If string_size > 0 Then
s = Space$(string_size + 1)
get_saved_string s, string_size + 1
End If
getDLLString = s
End Function
Public Sub test()
Debug.Print getDLLString(myfunc("hi there"))
End Sub