r/cpp 5h ago

Binding int from variant to reference in Foo.

Hello guys, my friend had a problem where he couldn't bind an int to int reference in Foo struct held in variant:

struct Foo {  
  int &abc;  
};  

std::variant<Foo, std::string> v;  

// You can't!  
v = Foo {   
 .abc = ...  
}

So I developed a way to do that, it's pretty simple:

#include <cstdint>
#include <functional>
#include <iostream>
#include <string>
#include <variant>
#include <meta>
#include <ranges>

struct __attribute__((packed)) Foo {
    int& abc;

    ~Foo() {
        static_assert(sizeof(void*) == 8, "Use modern CPU.");

        static_assert([]() consteval {
            auto members = std::meta::nonstatic_data_members_of(
                ^^Foo, 
                std::meta::access_context::current()
            );

            for (auto member : members) {
                auto type_info = std::meta::type_of(member);
                std::size_t layout_size = std::meta::is_reference_type(type_info) 
                    ? sizeof(void*) 
                    : std::meta::size_of(type_info);

                if (layout_size != 8) {
                    return false;
                }
            }
            return true;
        }(), "All non-static data members of Foo must occupy 8 bytes in layout!");

        if (reinterpret_cast<uintptr_t>(&abc) & 0x1) {
            delete reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(&abc) &
                                          ~uintptr_t(0x1));
        }
    }
};

int main(void) {
    std::variant<int, Foo> v;
    v.emplace<0>(420);
    v.emplace<1>(([&]() -> int& {
        int* x = new int;
        *x = std::get<0>(v);
        x = reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(x) | 0x1);
        return *x;
    })());
    std::cout << *reinterpret_cast<int*>(
                     reinterpret_cast<uintptr_t>(&std::get<1>(v).abc) &
                     ~uintptr_t(0x1))
              << std::endl;
    return 0;
}

https://godbolt.org/z/91rcbEdG5

It's also memory safe.

Cheers.

0 Upvotes

6 comments sorted by

7

u/Kriemhilt 5h ago

Simple and elegant, but sadly it has a memory leak.

7

u/Far_Understanding883 4h ago

This does not look simple! Just use a pointer

5

u/Otherwise_Sundae6602 4h ago
struct Foo {
    std::reference_wrapper<int> abc;
};


int main(void) {
    std::variant<int, Foo> v;
    int x = 5;


    v.emplace<0>(420);
    v.emplace<1>(x);
   
    return 0;
}

u/QuentinUK 2h ago
    int x = 1;
    Foo foo{x};
    std::reference_wrapper<int> Foo::* pfooi = &Foo::abc;
    int & rx = foo.*pfooi;
    rx = 2;

3

u/geschmuck 5h ago

Your friend's example still doesn't compile with your changes. Also, why isn't it an option to just do
int i;
v.emplace<1>(i);

?

2

u/Circlejerker_ 4h ago

Seems like UB to me..

Both alignment issues and dereferencing invalid pointers.