Skip to content

Unexpected behaviour with structs containing char** fields. #320

@Golui

Description

@Golui

Hi,

The following code behaves unexpectedly, probably because char** is ambiguous. It's an idiom in a codebase I am working with right now.

cppyy is at 3.5.0.

import cppyy
import cppyy.ll

cppyy.ll.set_signals_as_exception(True)

cppyy.cppdef("""
struct MyTestStruct
{
	char** name;
};

std::string theName = "This is a test";
char* theNameC = (char*)theName.c_str();
auto stc = MyTestStruct();
stc.name = &theNameC;
""")

print(cppyy.gbl.stc.name) # <cppyy.LowLevelView object at ...>
cppyy.gbl.std.string(cppyy.gbl.stc.name[0]) # I would expect string to be constructed, but instead it segfaults
*** Break *** segmentation violation
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[21], line 1
----> 1 cppyy.gbl.std.string(cppyy.gbl.stc.name[0])

TypeError: Template method resolution failed:
  none of the 13 overloaded methods succeeded. Full details:
  string::string(std::initializer_list<char> __il, const std::allocator<char>& __a) =>
    TypeError: takes at least 2 arguments (1 given)
  string::string(std::initializer_list<char> __il) =>
    SegmentationViolation: segfault in C++; program state was reset
  string::string(std::string&& __str, const std::string::allocator_type& __a) =>
    TypeError: takes at least 2 arguments (1 given)
  string::string(std::string&& __str) =>
    TypeError: could not convert argument 1
  string::string(const std::string::allocator_type& __a) =>
    TypeError: could not convert argument 1
  string::string(const std::string& __str, const std::string::allocator_type& __a) =>
    TypeError: takes at least 2 arguments (1 given)
  string::string() =>
    TypeError: takes at most 0 arguments (1 given)
  string::string(const std::string& __str) =>
    TypeError: could not convert argument 1
  string::string(const char* __s, std::string::size_type __n, const std::allocator<char>& __a) =>
    TypeError: takes at least 3 arguments (1 given)
  string::string(const char* __s, std::string::size_type __n) =>
    TypeError: takes at least 2 arguments (1 given)
  string::string(const std::string& __str, std::string::size_type __pos, const std::allocator<char>& __a = std::allocator<char>()) =>
    TypeError: takes at least 2 arguments (1 given)
  string::string(const std::string& __str, std::string::size_type __pos, std::string::size_type __n, const std::allocator<char>& __a = std::allocator<char>()) =>
    TypeError: takes at least 3 arguments (1 given)
  string::string(std::string::size_type __n, char __c) =>
    TypeError: takes at least 2 arguments (1 given)
  string::string(const char* __s) =>
    TypeError: could not convert argument 1 (bad argument type for built-in operation)

Naturally, I tried cppyy.ll.cast["char**"](cppyy.gbl.stc.name), but:

TypeError: Could not find "cppyy_cast<char**>" (set cppyy.set_debug() for C++ errors):
  none of the 3 overloaded methods succeeded. Full details:
  char** __cppyy_internal::cppyy_cast(char* val) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)
  char** __cppyy_internal::cppyy_cast(char* val) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)
  char** __cppyy_internal::cppyy_cast(char* val) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)
  none of the 4 overloaded methods succeeded. Full details:
  char** __cppyy_internal::cppyy_cast(char* val) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)
  char** __cppyy_internal::cppyy_cast(char* val) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)
  char** __cppyy_internal::cppyy_cast(char* val) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)
  char** __cppyy_internal::cppyy_cast(char* val) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)

Of course, this can be worked around with a pythonization:

from ctypes import POINTER, c_char_p
def replace_name(cls, name):
	if name != "MyTestStruct":
		return
	cls._name = cls.__dict__["name"]
	def name_impl(self):
		return cppyy.gbl.std.string(POINTER(c_char_p).from_address(cppyy.ll.addressof(self._name[0]))[0])
	cls.name = property(name_impl)

cppyy.py.add_pythonization(replace_name, "")

Is there a better way around this?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions