0

I'm wrapping a C interface and require to pass into it the address of a preallocated buffer. My wrapper places this buffer at a known location within wrapper object A. At runtime, I will be able to query the API for its buffer address. I need to be able to somehow get the wrapper object address however. Now this is what I've come up with:

  • This is a simplified testcase: std::array buf_ represents the buffer I'm passing into the C function.
  • The function glean_ptr() simulates the behavior of the API: it returns the pointer to the buffer.
  • I then want to be able to determine the address of the wrapping object A from the buffer address -> I packed it in a substruct and decrease its address.

DEMO

#include <iostream>
#include <array>

struct A
{
    A() : obj_{this} {}

#pragma pack(1)
    struct B {
        A* self_;
        std::array<int, 10> buf_;
    } obj_;

    auto glean_ptr() { return &obj_.buf_; }

    void say_hello() { std::cout << "hello" << std::endl; }
};

int main()
{
    A a;

    auto ptr = a.glean_ptr();

    // do some hacking

    A* b = reinterpret_cast<A*>(ptr)-1;

    b->say_hello();
}

I have two questions:

  • Will this always work? It works in the DEMO, but I'm planning to use it on a microcontroller (32 bit registers).
  • The compiler is not able to see through this I think. Might there a better way to achieve the same result without ugly reinterpret_cast?

EDIT according to @François Andrieux's suggestions (only array pointer arithmetics is defined in C++ standard):

DEMO

#include <iostream>
#include <array>

struct TCB_ // struct of which the C API returns a handle to
{
    int a;
    int b;
    char c;
    long long d;
};

struct A
{
    A() : buf_{this} {}

    static constexpr size_t S_ = (1+sizeof(TCB_)/sizeof(A*) + !!(sizeof(TCB_)%sizeof(A*)));
    std::array<A*, S_> buf_;

    TCB_* glean_ptr() { return reinterpret_cast<TCB_*>(&buf_[1]); }

    void say_hello() { std::cout << "hello" << std::endl; }
};

int main()
{
    A a;

    auto ptr = a.glean_ptr();

    // do some hacking

    A* b = reinterpret_cast<A*>(ptr)-1;

    b->say_hello();
}
glades
  • 3,778
  • 1
  • 12
  • 34
  • you forgot to undo your pragma – Sergey Kolesnik Apr 06 '22 at 18:12
  • You can't use pointer arithmetic to get a member from a pointer to another member. There is no way to iterate or navigate from member to member. You could try an array of pointer-to-members but you still need a pointer or reference to the instance, there is no way to get the owning object back from a pointer or reference to a member. – François Andrieux Apr 06 '22 at 19:05
  • @FrançoisAndrieux but i just did that and it works? Why can't you use pointer arithmetic? – glades Apr 06 '22 at 20:49
  • @glades C++ doesn't make any guaranties regarding your pointer arithmetic. It worked when you tried it, on your system basically, but that is basically a coincidence. With different build flags or with a different compiler or on a different platform, it could break for no apparent reason. You might update your compiler one day and your code will stop working. And compilers are allowed to assume your code doesn't break any rules, and optimize accordingly. Your pointer arithmetic breaks the rules and compilers can apply surprising transformations, resulting in your approach breaking. – François Andrieux Apr 06 '22 at 21:43
  • @glades C++ only defines pointer arithmetic for pointers to elements of arrays, and only as long as the result would point to a different element in the same array (or one-past-the-end). – François Andrieux Apr 06 '22 at 21:43
  • @FrançoisAndrieux Ok I understand this is UB. But my only solution would be to create an array of 2 [reinterpret_cast>(this), std::array]? I also found this what makes me want ot avoid pragma packing my struct up: https://devblogs.microsoft.com/oldnewthing/20200103-00/?p=103290. The other solution would be to store the std::array in an array of [A*, A*...] and extend the array until I can fit std::array into the second element to save space. – glades Apr 07 '22 at 05:05
  • @FrançoisAndrieux See my edited answer – glades Apr 07 '22 at 05:17
  • You can't use `reinterpret_cast` to pretend a pointer to one object is a pointer to another unrelated type. It is UB to use the result of `reinterpret_cast(&buf_[1]);` except to cast it back to the original type. If `reintrepret_cast` looks like it solves your problem, then it is probably also UB. For standard layout types, you can try [Get pointer to object from pointer to some member](https://stackoverflow.com/questions/33870219/get-pointer-to-object-from-pointer-to-some-member) which falls in a grey zone of what is allowed or not. – François Andrieux Apr 07 '22 at 13:35
  • Beware that needing to do this is a good indicator that there is a design error in your solution. – François Andrieux Apr 07 '22 at 13:38
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/243691/discussion-between-glades-and-francois-andrieux). – glades Apr 07 '22 at 14:32

0 Answers0