Const data C++ -> Python

Hello,

I wrote some shared libraries in C++ that I am trying to use in python, using

ROOT.gSystem.Load()

I can get everything to run fine, but at one point I have a function

int getEntry(const LUTKey&k, const LUTEntry*& e) const;

which returns an int depending on if it failed or not, and sets the pointer “e” to something. python complains that “e” must be initialized beforehand, but I’m not sure how to do this. Any tips and or better ideas on how to use C++ libraries in python? Thanks


_ROOT Version: 6.14
_Platform: debian
_Compiler: g++


Hi,
you are taking a const reference to a pointer and then trying to change the pointer, that doesn’t sound ok.
As a first approximation you might want to remove the const-ness, but I’m not sure how PyROOT supports references to pointers (@etejedor might know).

If everything else fails, you can always change the function signature to take a reference to an ad-hoc type that encapsulates a pointer instead of a reference to pointer.

Hope this helps!
Enrico

Hi @BrownPants,

PyROOT converts both references and pointers in C++ to references in Python. How are you making this call from Python? What are you passing as second parameter of getEntry?

Enric

@eguiraud This code works in C++ fine, I intend to be able to change the pointer itself, but not change any of the data that it points to, which I believe is how this should be done.

@etejedor I’m running the code in python as

key = r.LUTKey(event.patternId, event.ccId) 

#this code works, and creates the correct class

if lut.getEntry(key, entry):
  exit() 

#lut is the class which has getEntry as it's member function, the class gets created correctly

If I don’t initial instantiate entry, which gives me an error:

Traceback (most recent call last):
  File "makeChi2Plots.py", line 33, in <module>
    if lut.getEntry(key, entry):
NameError: name 'entry' is not defined

I tried with an empty constructor

entry = r.LUTEntry()
if lut.getEntry(key, entry):

but get

free(): invalid pointer
Aborted

which I’m guessing has to do with python trying to free the memory, but C++ not allowing it to.

Thanks again

Hi,
You need to call the method passing something that exists as parameter, otherwise Python complains about unknown variable as you saw.

What you can try is:

k = ...
entry = cppFunctionThatReturnsAPointerToLUTEntry()
getEntry(k, entry)

If that doesn’t work, please consider Enrico’s suggestion about the encapsulation.

Enric

@eguiraud This code works in C++ fine, I intend to be able to change the pointer itself, but not change any of the data that it points to, which I believe is how this should be done.

sorry, I parsed it as LUTEntry* const & e :man_shrugging:

In the first example as written entry is never initialized so "name entry is not defined" seems kosher.
In the second example with the empty constructor I think you are confusing PyROOT’s lifetime management by creating an object “on the stack” and then passing it to a function that takes it as a pointer.

You can create the entry object as a pointer in a C++ helper function.
This works:

// entry.h
struct Entry {
   int v;
};

const Entry *GetPtr()
{
   return new Entry{0};
}

void ChangePtr(const Entry *&p)
{
   p = new Entry{1};
}

and then in python

>>> ROOT.gInterpreter.Declare('#include "entry.h"')
>>> p = ROOT.GetPtr()
>>> >>> e.v
0
>>> ROOT.ChangePtr(e)
>>> e.v
1

This is a clever way to do it. I’ll let you know if it works. Thanks!

As a more high-level suggestion, since you are writing the C++ code yourself, my personal experience is that adhering to modern C++ practices and preferring value semantics to pointer semantics, designing your types so that they can be passed around as values or references and your interfaces so that they do not deal with raw pointers, not only reduces the amount of issues in the C++ code itself but makes that code more usable from python. Just my two cents :smiley:

@eguiraud: your code leaks.

If you want the C++ side to fill in a pointer, you start with a nullptr, just like in C++.

key = r.LUTKey(event.patternId, event.ccId) 
entry = r.MakeNullPointer(r.LUTEntry)
lut.getEntry(key, entry)

where I assume you did import ROOT as r.

@eguiraud: your code leaks

depends what the rest of the logic does :wink: you understand the point I was making…

This worked and I think was my favorite suggestion. Thanks!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.