Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing addresses to members #184

Open
very-cool-name opened this issue Sep 16, 2024 · 5 comments
Open

Accessing addresses to members #184

very-cool-name opened this issue Sep 16, 2024 · 5 comments
Labels
help wanted PR with a fix wouod be appreciated

Comments

@very-cool-name
Copy link

Hey, I have some pybinded structs, that have a lot of repeating boilerplate:

struct MyStruct {
  int x;
  char c;
};

py::class_< MyStruct >(module, "MyStruct")
  .def_readonly("x", &MyStruct::x)
  .def_readonly("c", &MyStruct::c);

I was thinking about using boost::pfr to automate this, but I don't think there is currently any way, since boost::pfr doesn't have an interface to access member addresses i.e. something like this:

auto cls = py::class_< MyStruct >(module, "MyStruct");
boost::pfr::for_each_member_address_with_name<MyStruct>([cls](std::string_view name, auto address) {
  cls.def_readonly(name, address);
});

Is there any interest in having such interface? And if so - I think I can make a PR. If you could give me some hints on possible implementation - that would be greatly appreciated.

Thanks!

@apolukhin apolukhin added the help wanted PR with a fix wouod be appreciated label Oct 17, 2024
@apolukhin
Copy link
Member

Looks quite interesting. Please provide a PR.

@very-cool-name
Copy link
Author

Gave it some thought and I don't really know how to implement it. Structured bindings won't help here, since essentially:

struct S { char c; };

S s;
auto& [c] = s;
// there is no way to get from ref c to char S::*

If you are aware of maybe compiler specific tricks that can let me do that - please help me.

@hansalemaos
Copy link

hansalemaos commented Nov 7, 2024

Maybe something like this? Probably not the safest version, but it works :)

template <typename Ts, typename Us>
void change_data_without_a_check(Ts& mystruct, const std::string_view column_name, const Us new_data) {
	size_t column_id = 0;
	constexpr auto field_names = pfr::names_as_array<Ts>();
	for (size_t i = 0; i < field_names.size(); i++) {
		if (field_names[i].compare(column_name) == 0) {
			column_id = i;
		}
	}
	size_t i = 0;

	pfr::for_each_field(mystruct, [&](auto& f, auto meta) {
		if (i == column_id) {
			void* voidptr = reinterpret_cast<void*>(&(pfr::get<meta>(mystruct))); // this part gives me the mem address of each field (msvc - c++20)
			(*(Us*)(voidptr)) = new_data; // ... and some "don't try this at home" assignments
		}
		i++;

		});

}
struct S { char c; uint8_t u; };
int main(int argc, const char* argv[]) {

	auto s2x = S('A');
	uint8_t newvaluex = 100;
	std::string mycolumnx = "c";
	change_data_without_a_check(s2x, mycolumnx, newvaluex);
	char newvaluex2 = 'A';
	std::string mycolumnx2 = "u";
	change_data_without_a_check(s2x, mycolumnx2, newvaluex2);
	std::cout << s2x.c << " " << s2x.u << std::endl; } ```

@very-cool-name
Copy link
Author

As far as I understand, in the code above there is no point at which we have pointer of type Us Ts::*.
This reinterpret_cast<void*>(&(pfr::get<meta>(mystruct))); is indeed the memory address, but I don't know of any way to make a hop from memory address to a pointer-to-member.

@hansalemaos
Copy link

Got it, I thought your main concern was getting the memory address of each member.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted PR with a fix wouod be appreciated
Projects
None yet
Development

No branches or pull requests

3 participants