How can I query the data of a complex return type using PyObject_CallObject? - c++

I have a small python programm that computes a 2d positon
def getPosition(lerpParameter):
A = getClothoidConstant()
...
The python script can be found in my project folder here.
The function getPosition is included in the file clothoid.py. The function returns a value of type Vector2. How can I access the data of the returned Vector2d in my C++ program?
The C++ programm looks like this:
/// Get function
PyObject *pFunc = PyObject_GetAttrString(pModule, pid.functionName);
// pFunc is a new reference
if (pFunc && PyCallable_Check(pFunc))
{
PyObject *pArgs = PyTuple_New(pid.getArgumentCount());
PyObject *pValue = nullptr;
// setup function parameters
for (int i = 0; i < pid.getArgumentCount(); ++i)
{
if (pid.arguments[i].type == eType::Int)
{
pValue = PyLong_FromLong(atoi(pid.arguments[i].name.c_str()));
}
else
{
pValue = PyFloat_FromDouble(atof(pid.arguments[i].name.c_str()));
}
if (!pValue)
{
Py_DECREF(pArgs);
Py_DECREF(pModule);
std::cout << "Cannot convert argument" << std::endl;
throw std::runtime_error("Cannot convert argument");
}
// pValue reference stolen here:
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != nullptr)
{
switch (pid.returnType)
{
...
case eType::Double:
//std::cout << "Result of call: " << PyFloat_AsDouble(pValue) << std::endl;
doubleValue_ = PyFloat_AsDouble(pValue);
break;
case eType::Vector2d:
{
// How can I acccess the data here?
}
break;
default:
break;
}
Py_DECREF(pValue);
}
If the return type is a double or int it is easy to get the corresponding value. But I have no idea how to access the data of the Vector2.

If you want to get the 'x' attribute, and you know it's a float:
PyObject *temp = PyObject_GetAttrString(pValue, "x");
if (temp == NULL) {
// error handling
}
double x = PyFloat_AsDouble(temp);
// clean up reference when done with temp
Py_DECREF(temp);

Related

How to read in a .cfg.txt file

I've been trying to read this file for some time now and tried about everything I could think of. I placed the file in my Products folder and In my resource folder and included (ResourcePath + "File.cfg.txt") and neither worked. I'd appreciate it if someone could tell me what I'm missing and where to put this file to read it in. Again I'm using Xcode and the SFML library with it.
Keys.cfg.txt
Window_close 0:0
Fullscreen_toggle 5:89
Move 9:0 24:38
/////////////////////////////////////////
.CPP
#include "EventManager.h"
using namespace std;
EventManager::EventManager(): m_hasFocus(true){ LoadBindings(); }
EventManager::~EventManager(){
for (auto &itr : m_bindings){
delete itr.second;
itr.second = nullptr;
}
}
bool EventManager::AddBinding(Binding *l_binding){
if (m_bindings.find(l_binding->m_name) != m_bindings.end())
return false;
return m_bindings.emplace(l_binding->m_name, l_binding).second;
}
bool EventManager::RemoveBinding(std::string l_name){
auto itr = m_bindings.find(l_name);
if (itr == m_bindings.end()){ return false; }
delete itr->second;
m_bindings.erase(itr);
return true;
}
void EventManager::SetFocus(const bool& l_focus){ m_hasFocus = l_focus; }
void EventManager::HandleEvent(sf::Event& l_event){
// Handling SFML events.
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
EventType sfmlEvent = (EventType)l_event.type;
if (e_itr.first != sfmlEvent){ continue; }
if (sfmlEvent == EventType::KeyDown || sfmlEvent == EventType::KeyUp){
if (e_itr.second.m_code == l_event.key.code){
// Matching event/keystroke.
// Increase count.
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else if (sfmlEvent == EventType::MButtonDown || sfmlEvent == EventType::MButtonUp){
if (e_itr.second.m_code == l_event.mouseButton.button){
// Matching event/keystroke.
// Increase count.
bind->m_details.m_mouse.x = l_event.mouseButton.x;
bind->m_details.m_mouse.y = l_event.mouseButton.y;
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else {
// No need for additional checking.
if (sfmlEvent == EventType::MouseWheel){
bind->m_details.m_mouseWheelDelta = l_event.mouseWheel.delta;
} else if (sfmlEvent == EventType::WindowResized){
bind->m_details.m_size.x = l_event.size.width;
bind->m_details.m_size.y = l_event.size.height;
} else if (sfmlEvent == EventType::TextEntered){
bind->m_details.m_textEntered = l_event.text.unicode;
}
++(bind->c);
}
}
}
}
void EventManager::Update(){
if (!m_hasFocus){ return; }
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
switch (e_itr.first){
case(EventType::Keyboard) :
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Mouse) :
if (sf::Mouse::isButtonPressed(sf::Mouse::Button(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Joystick) :
// Up for expansion.
break;
default:
break;
}
}
if (bind->m_events.size() == bind->c){
auto callItr = m_callbacks.find(bind->m_name);
if(callItr != m_callbacks.end()){
callItr->second(&bind->m_details);
}
}
bind->c = 0;
bind->m_details.Clear();
}
}
void EventManager::LoadBindings(){
std::string delimiter = ":";
std::ifstream bindings;
bindings.open("keys.cfg");
if (!bindings.is_open()){ std::cout << "! Failed loading keys.cfg." << std::endl; return; }
std::string line;
while (std::getline(bindings, line)){
std::stringstream keystream(line);
std::string callbackName;
keystream >> callbackName;
Binding* bind = new Binding(callbackName);
while (!keystream.eof()){
std::string keyval;
keystream >> keyval;
int start = 0;
int end = keyval.find(delimiter);
if (end == std::string::npos){ delete bind; bind = nullptr; break; }
EventType type = EventType(stoi(keyval.substr(start, end - start)));
int code = stoi(keyval.substr(end + delimiter.length(),
keyval.find(delimiter, end + delimiter.length())));
EventInfo eventInfo;
eventInfo.m_code = code;
bind->BindEvent(type, eventInfo);
}
if (!AddBinding(bind)){ delete bind; }
bind = nullptr;
}
bindings.close();
}
The problem is that you have to copy the respective files in the bundle, and that only objective-c can provide you the full name of the file on the destination device.
To overcome this, make a .mm-file and place a c++ trampoline function in there, which gives you the full path (see code below).
One pitfall can be that you have to make sure that the config- and text files like "keys.cfg" are actually copied into the bundle. Select the respective file in the project and open the property inspector; make sure that - the respective target in "Target Membership" is checked.
// File: myFileNameProvider.mm
#import <Foundation/Foundation.h>
#include <iostream>
std::string GetTextureFilename(const char *name)
{
NSString *nameAsNSString = [NSString stringWithUTF8String:name];
NSString *fullName = [[NSBundle mainBundle]
pathForResource:nameAsNSString ofType: nil];
if (fullName)
return std::string([fullName UTF8String]);
else
return "";
}
Then, in your CPP-code, declare the signature of std::string GetTextureFilename(const char *name), and before opening the file get the full path by calling it:
// MyCPPFile.cpp
#include <iostream>
#include <fstream>
// declaration:
std::string GetTextureFilename(const char *name);
void myC_Func {
std::string fullPath = GetTextureFilename("keys.cfg");
std::ifstream bindings;
bindings.open(fullPath.c_str());
if (!bindings.is_open()) {
std::cout << "! Failed loading keys.cfg." << std::endl;
}
...
}

Passing information between SWIG in and freearg typemaps

I have a typemap targetting Python which accepts both an already wrapped pointer object or additionally allows passing a Python sequence. In the case of a wrapped pointer, I do not want to delete the memory as SWIG owns it. However, when processing a sequence I'm allocating a temporary object that needs to be deleted. So I added a flag to my 'in' typemap to mark whether I allocated the pointer target or not. How can I access this flag in the corresponding 'freearg' typemap?
The typemaps look like this:
%typemap(in) name* (void* argp = 0, int res = 0, bool needsDelete = false) {
res = SWIG_ConvertPtr($input, &argp, $descriptor, $disown | 0);
if (SWIG_IsOK(res)) {
$1 = ($ltype)(argp); // already a wrapped pointer, accept
} else {
if (!PySequence_Check($input)) {
SWIG_exception(SWIG_ArgError(res), "Expecting a sequence.");
} else if (PyObject_Length($input) != size) {
SWIG_exception(SWIG_ArgError(res), "Expecting a sequence of length " #size);
} else {
needsDelete = true;
$1 = new name;
for (int i = 0; i < size; ++i) {
PyObject* o = PySequence_GetItem($input, i);
(*$1)[i] = swig::as<type>(o);
Py_DECREF(o);
}
}
}
}
%typemap(freearg) name* {
if ($1 /* && needsDelete */) delete $1;
}
This leads to code being generated that looks like:
{
res2 = SWIG_ConvertPtr(obj1, &argp2, SWIGTYPE_p_MyName_t, 0 | 0);
if (SWIG_IsOK(res2)) {
arg2 = (MyName *)(argp2); // already a wrapper pointer, accept
} else {
if (!PySequence_Check(obj1)) {
SWIG_exception(SWIG_ArgError(res2), "Expecting a sequence.");
} else if (PyObject_Length(obj1) != 3) {
SWIG_exception(SWIG_ArgError(res2), "Expecting a sequence of length ""3");
} else {
needsDelete2 = true;
arg2 = new MyName;
for (int i = 0; i < 3; ++i) {
PyObject* o = PySequence_GetItem(obj1, i);
(*arg2)[i] = swig::as<double>(o);
Py_DECREF(o);
}
}
}
}
if (arg1) (arg1)->someMember = *arg2;
resultobj = SWIG_Py_Void();
{
if (arg2 /* && needsDelete */) delete arg2;
}
According to 11.15 Passing data between typemaps from the SWIG manual:
You just need to use the variable as needsDelete$argnum in the freearg typemap.

Extending python3, how does the garbage collection work

I'm making my own PriorityQueue in C as a python module. I read the basics of python ownership and reference system, so I thought I'd do the following:
In push(): Accept an priority(int) and an object to be saved. Increment the reference count on the object to be saved, since we will be keeping that.
In pop(): Delete the object from my priorityqueue, but don't decrement the reference counter, since that might destroy the object. Instead I transfer my reference ownership to the python function calling my function.
This seemed to work at first hand. But when actually using it in an application I get the following error:
Fatal Python error: GC object already tracked
What does this mean? The stacktrace is not useful at all, it's all inside python files I don't recognize(sre_parse and apport_python_hook).
Just for clarity, these are my C push and pop functions:
(self->heap[index]->key is the priority of the element at that index
self->heap[index]->value is the object)
PyObject* pop(CDSHeap *self) {
//If there aare no elements
if (self->heap[0].value == 0 || self->end == 0) {
Py_RETURN_NONE;
}
//If there is only one element
if (self->end == 1) {
PyObject* result = self->heap[0].value;
self->heap[0].key = 0;
self->end = 0;
return result;
}
//Two or more elements:
//First save the result:
PyObject* result = self->heap[0].value;
//Get the last element, and place it at the top
while (self->heap[self->end].value == 0) self->end--;
self->heap[0].value = self->heap[self->end].value;
self->heap[0].key = self->heap[self->end].key;
self->heap[self->end].value = 0;
//Reheapify the heap
int ptr = 0;
while (self->end >= ptr) {
if (self->heap[ptr*2+1].value != 0 && self->heap[ptr*2+1].key < self->heap[ptr].key
&& (self->heap[ptr*2+2].value == 0 || self->heap[ptr*2+1].key <= self->heap[ptr*2+2].key)) {
swapElement(self->heap, ptr, ptr*2+1);
ptr = ptr*2+1;
}else
if (self->heap[ptr*2+2].value != 0 && self->heap[ptr*2+2].value < self->heap[ptr].value) {
swapElement(self->heap, ptr, ptr*2+2);
ptr = ptr*2+2;
} else {
break;
}
}
return result;
}
PyObject* push(CDSHeap *self, PyObject* args) {
int k;
PyObject *obj;
if (!PyArg_ParseTuple(args, "iO",&k, &obj)){
return NULL;
}
Py_INCREF(obj);
//Add the element to the end of the heap
self->heap[self->end].key = k;
self->heap[self->end].value = obj;
//Increment the size and reheapify
int ptr = self->end++;
while (ptr > 0) {
int parent = (ptr-1)/2;
if (self->heap[ptr].key < self->heap[parent].key) {
swapElement(self->heap, ptr, parent);
ptr = parent;
} else {
Py_RETURN_NONE;
}
}
Py_RETURN_NONE;
}

how to pass an array from c++ to python function and retrieve python returned array to c++

I'm writting a c++ program to call a python function and retrieve the return array.but i always get an error as below:
only length-1 arrays can be converted to Python scalars
and my c++ code:
int main(int argc, char *argv[])
{
int i;
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pValue;
if (argc < 3)
{
printf("Usage: exe_name python_source function_name\n");
return 1;
}
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyString_FromString(argv[1]);
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, argv[2]);
if (PyCallable_Check(pFunc))
{
// Prepare the argument list for the call
if( argc > 3 )
{
pArgs = PyTuple_New(argc - 3);
for (i = 0; i < argc - 3; i++)
{
pValue = PyInt_FromLong(atoi(argv[i + 3]));
if (!pValue)
{
PyErr_Print();
return 1;
}
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
if (pArgs != NULL)
{
Py_DECREF(pArgs);
}
} else
{
pValue = PyObject_CallObject(pFunc, NULL);
}
if (pValue != NULL)
{
cout <<pValue;
printf("Return of call : %ld\n", PyInt_AsLong(pValue));
PyErr_Print();
Py_DECREF(pValue);
}
else
{
PyErr_Print();
}
} else
{
PyErr_Print();
}
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);
// Finish the Python Interpreter
Py_Finalize();
system("PAUSE");
return 0;
}
Python function:
import numpy as np
_ZERO_THRESHOLD = 1e-9 # Everything below this is zero
def data():
print "process starting..."
N = 5
obs = np.matrix([np.random.normal(size=5) for _ in xrange(N)])
V = pca_svd(obs)
print "V:"
print V[0:5]
pca = IPCA(obs.shape[1], 3)
for i in xrange(obs.shape[0]):
x = obs[i,:].transpose()
print " "
print "new input:"
print x
pca.update(x)
U = pca.components
A = pca.variances
B = U.T*x
print B
return B
I know there is something wrong with this statement
PyInt_AsLong(pValue)
Can anyone tell me how to fix that in order to retrieve the matrix from python to c++
Thank you so much.
You are using PyInt_AsLong(pValue) to convert the Python object pValue to a C long scalar. If pValue is a Numpy array, this means that you are trying to convert the array to a number, which is only possible for length-1 arrays:
only length-1 arrays can be converted to Python scalars
Instead of using PyInt_AsLong, use the PyArray_* functions provided by Numpy's C API to access the data; in particular, see section Array API.
You want to use NumPy C API, probably to get a data pointer with
#include <numpy/arrayobject.h>
...
p = (uint8_t*)PyArray_DATA(pValue);
after making really sure that you actually got an array of the right dimensions. See my hello.hpp for some example code.

std::list iterator

The following code instead of returning a pointer back to an audioResource it returns
something else which is invalid, i've gone through with a debugger and the problem is with this line
return *list_it;
Here is my function:
AudioResource* AudioManager::getResource(const unsigned int ID)
{
std::list<AudioResource*>::iterator list_it;
for(list_it = m_resources.begin(); list_it!= m_resources.end(); list_it++)
{
if((*list_it)->getID()==ID)
{
std::cout<<*(*list_it)->getFileName();
return *list_it;
}
}
return nullptr;
}
O and I have tried putting it as (*list_it) but i got the same results =s
How it is populated...
Resource* AudioManager::addResource(const unsigned int ID,
const std::string fileName, const unsigned int scope,
const std::string type)
{
AudioResource* temp;
if(type == "AUDIO_TYPE_SAMPLE")
{
temp = new AudioResource(ID,fileName,scope,
RESOURCE_AUDIO,AUDIO_TYPE_SAMPLE);
m_resources.push_back(temp);
}
else if(type == "AUDIO_TYPE_STREAM")
{
temp = new AudioResource(ID,fileName,scope,
RESOURCE_AUDIO,AUDIO_TYPE_STREAM);
m_resources.push_back(temp);
}
return temp;
}
call to get resource
cout<<AudioManager::getInstance()->getResource(IDnum)->getFileName();
If type is neither of the two values an uninitialized pointer is added to m_resources:
AudioResource* temp;
if(type == "AUDIO_TYPE_SAMPLE")
{
temp = new AudioResource(ID,fileName,scope,RESOURCE_AUDIO,AUDIO_TYPE_SAMPLE);
}
else if(type == "AUDIO_TYPE_STREAM")
{
temp = new AudioResource(ID,fileName,scope,RESOURCE_AUDIO,AUDIO_TYPE_STREAM);
}
m_resources.push_back(temp);
Initialize temp to NULL and only add to m_resources if temp != NULL.
Also, the function returns the same uninitialized pointer.
You return nullptr in case the ID doesn't exist, but you never check against it at the call site, which will give you a null pointer access if the ID doesn't exist and which will likely create problems.
AudioManager::getInstance()->getResource(IDnum)->getFileName();
Change that to
AudioResource* res = AudioManager::getInstance()->getResource(IDnum);
if(res)
std::cout << res->getFileName();