2

I've got a C DLL that exposes a handful of methods that return void pointers to a Class like so:

void *GetLicense() {
    static AppLicenseImpl ipds_;
    return (void *) &ipds_;
}

In C++, after loading the DLL, I'd do this to work with it:

typedef void *(* FPGetLicense)();
GetLicense_ = (FPGetLicense)GetAddress("GetLicense");
license_ = (AppLicense *) GetLicense_();
license_->GetApplicationStatus(); // Load data so that other calls don't fail

I can't figure out how to parallel that in Python. This gets me the pointer:

d = ctypes.cdll.LoadLibrary('license.dll')
d.GetLicense.restype = ctypes.c_void_p
p = d.GetLicense() # returns ptr loc, something like 8791433660848L

But I obviously can't call p.GetApplicationStatus() in Python. Does anyone have a suggestion on how I'd instantiate that Class the rest of the way in Python so that I can call GetApplicationStatus()?

7
  • What do you mean by "instantiate that class the rest of the way"? The instance has already been fully instantiated when GetLicense returns—at least it had better be, or the next line of C++ is going to segfault. Commented Oct 15, 2013 at 19:16
  • My apologies, my terminology is probably bad. Does that require that there be an analogous class definition in my Python module that reflects the AppLicense class in the c/c++ code? Commented Oct 15, 2013 at 19:28
  • Well, your C code can't possibly have any reference to the AppLicense class. On top of that, there is no real portable way to reference C++ types from a .DLL/.so in C++ in the first place; this is why you generally have extern "C" wrappers that take a struct or void* as the first argument in the first place. But if you want to access members of a struct or class from ctypes, then yes, you need to define a ctypes.Struct. If you don't want to do that, consider using cffi or building custom bindings (possibly with Cython) instead of using ctypes. Commented Oct 15, 2013 at 20:01
  • You can make AppLicense a subclass of c_void_p. Then the instance self is a pointer to the C++ object. Add methods that wrap the extern "C" stubs that call the C++ methods. Add a _check_retval_ method to centralize error checking the class when it's used as a restype. Commented Oct 15, 2013 at 21:42
  • 1
    Export an extern "C" function such as AppLicense_GetApplicationStatus that takes an AppLicense * and calls the GetApplicationStatus method. The Python AppLicense class (a subclass of c_void_p) has a GetApplicationStatus method that calls d.AppLicense_GetApplicationStatus(self). You'd set d.GetLicense.restype = AppLicense, etc. As abarnert mentioned, you may want to look into alternatives for wrapping C++ libraries such as Boost.Python or SWIG. Commented Oct 15, 2013 at 23:24

2 Answers 2

4

Quoting from the docs:

Sometimes you have instances of incompatible types. In C, you can cast one type into another type. ctypes provides a cast() function which can be used in the same way.

So, the Python equivalent of the C++ code is:

license = cast(d.GetLicense(), ctypes.POINTER(AppLicense))
license.GetApplicationStatus()

However, often this isn't necessary; you may be able to just do this:

d.GetLicense.restype = ctypes.POINTER(AppLicense)

This looks like "cheating", but it really isn't. You're just telling it to call the POINTER(AppLicense) constructor with the result. And since POINTER(AppLicense) is a ctypes data type, it will not have to first assume the result is a C int.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I think. I am probably missing some scaffolding code in my python module for the class. I'll do some additional reading and maybe update the question when I'm closer / have better tracebacks or can more succinctly ask the question.
1

I spent more time with this - from c++ when I want to work with the class instance the void pointer refers to I do something like this:

class AppLicense {
public:
    AppLicense() {}
    virtual LicenseStatus GetApplicationStatus() = 0;
}

But I couldn't figure out how to do that in Python. This does not work:

class AppLicense(object):
  def GetApplicationStatus(self):
    pass

So instead I exported another function in the dll like this:

extern "C" {
    int P_GetApplicationStatus(void *ptr) {
        try {
            AppLicenseImpl * ref = reinterpret_cast<AppLicenseImpl *>(ptr);
            return ref->GetApplicationStatus();
        } catch (...) {
            return 0; // License Error default.
        }
    }
}

Once I had that in place, using it from Python is accomplished like this:

d.GetLicense.restype = ctypes.c_void_p
p = d.GetLicense()

d.C_GetApplicationStatus.argtypes = [ctypes.c_void_p]
status = d.P_GetApplicationStatus(p)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.