Accessing C++ Data Structures, Containers from Python

Hi all,

This is another basic question and the title says it all. In my initial experimentation with mixing C++ and Python, I’ve been able to access, from the python-side, int, float and char data types with success, but whenever I try and access a container like a vector, this is the error yielded:

AttributeError: 'genModule.TestClass' object has no attribute 'group_of_ints'

From this code, on the C++ side:

class TestClass{
PUBLISHED:
	int firstTest;
	NodePath workingNp;
	std::vector<int> group_of_ints;
	void modifyFirstTestValue(int newVal, NodePath& sentNp)
	{
		firstTest = newVal;
		workingNp = sentNp;
		NodePath dummy_node= sentNp.attach_new_node("Dummy Node Name");

	}

	NodePath returnDummyNp(NodePath sentParentNp, LVecBase3* sentVec)
	{
		NodePath dummy_node = sentParentNp.attach_new_node("fillMeUp");
		sentParentNp.set_pos(sentVec->get_x(), sentVec->get_y(), sentVec->get_z());
		return dummy_node;
	}
};

The python side that uses this module:

instTest=genModule.TestClass()
instTest.firstTest=5
instTest.modifyFirstTestValue(3324,render)
b3=LVecBase3(1.000,300.000,0.000)
npDummi=instTest.returnDummyNp(traker,b3)
print(instTest.group_of_ints)#<this yields the error...

I also noticed that I can’t play around with the vector container from the C++ side like this:

group_of_ints.assign(5,1)

Yields this error from the C++ side:

  /c/Panda3D-1.10.10-x64/P3DModuleBuilder-master/P3DModuleBuilder-master/source/example.h:31:2:
  error: unknown type 'group_of_ints'

        group_of_ints.assign(5,1)

   ^~~~~~~~~~~~~


  /c/Panda3D-1.10.10-x64/P3DModuleBuilder-master/P3DModuleBuilder-master/source/example.h:31:15:
  error: syntax error, unexpected '.'

        group_of_ints.assign(5,1)

So how can this be resolved? Moreso, what ways are typically used to initiate containers, modify them, pass them (or their contents) back and forth between Python and C++ in Panda3D? It doesn’t necessarily have to be vector containers.

Thanks and sorry for these basic questions…

After a little bit of digging, I’ve found out that vectors aren’t supported:

So vector is implemented by wrapping it into a template class, which I’ve found is PointerToArray. Adding, removing, reading , etc, all pertinent methods are documented here:
https://www.panda3d.org/reference/cxx/classPointerToArray.html#a7ae374aa0dea6ac59d8b185fcc27e713

One last question though, how would one access the data stored in the PointerToArray object from the python side, once it’s returned as a “block of raw data in a string”?

//C++:
	string return_potential_list()
	{
        group_of_ints.push_back(4);
		return group_of_ints.get_data();
	}

#Python:
potentialListOfInts=instTest.return_potential_list()

Is there any way to convert the returned string into a legible list of integers?

EDIT:

This worked for the conversion:

potentialListOfInts=instTest.return_potential_list()
intListByteString=bytes(potentialListOfInts, "utf8")
intList=list(intListByteString)

print(intList)
#This command yields: [4, 0, 0, 0]

I hope this helps people who are just venturing into mixing C++ and Python somehow.

Thanks.

2 Likes

While I don’t have much experience on the C++ side of things, this looks wrong on the Python side. You are decoding the string as utf-8 which is for text. You are getting [4, 0, 0, 0] for the integer 4 because 32-bit integers are 4 bytes long and you’re converting each byte into a number. Try a large number or a negative number and you’ll see the issue more clearly.

I’d use the array module. Use typecode ‘i’ for signed ints (see the array module documentation for other types).

from array import array
...
potentialListOfInts = instTest.return_potential_list()
intList = array('i', potentialListOfInts)

Unless you need to do something that specifically requires a list, you can probably use the array as-is in the rest of your code. You can iterate, insert, append, etc. just like a list. Otherwise it has a tolist() method.

2 Likes

Hi, thanks for pointing that out. I actually took a second look and realized that I was doing multiple things incorrectly. The rest of the C++ works fine when I use the PointerToArray template in lieu of a vector container, but the string returned from get_data() isn’t even going to be properly recognized on the Python side, since it’s a C++ type and hence some conversion was needed:

//Correct C++:
	PyObject* return_potential_list()
	{
		string stringObj = group_of_ints.get_data();
		const char* constPntr= stringObj.c_str();
		PyObject* gotString = PyBytes_FromString(constPntr);
		return gotString;
	}

Then on the python side, following your clarification:

potentialListOfInts = instTest.return_potential_list()
intList = array('i', potentialListOfInts)

I tried with negative numbers and it works properly. Thank you so much for the clarification! :grinning_face_with_smiling_eyes:

It would theoretically be possible to tell interrogate to wrap the vector type, using a forcetype declaration, or by putting PUBLISHED: in the parser-inc/vector file.

If you want to return a bytes object from C++, use the vector_uchar type; that one is wrapped automatically as bytes by interrogate.

3 Likes