#include <iostream>
#include <stdio.h>
#include <string>

struct A
{
    A(const std::string& pa) : a(pa) {printf("CTR: A address: %p\n", this) ;}
    std::string a;
};

struct B
{
    B(const std::string& pb) : b(pb) {printf("CTR: B address: %p\n", this) ;}
    std::string b;
};

// assumed data layout:
struct C {

    C() : a("astring"), b("bstring") {}
  // ...
  A a;
  // ...
  B b;
};

void approach1( A *pa, B *pb )
{

    printf("approach1: A address: %p B address: %p\n", pa, pb); 
    // compute offset:
    std::ptrdiff_t offset = static_cast<char*>((void*)pb) - static_cast<char*>((void*)pa);
    // then in some other function...
    // given offset and ptr to b, compute ptr to a:
    A *a = static_cast<A*>( (void*)(static_cast<char*>((void*)pb) + offset) );
    printf("approach1: a address: %p \n", a); 

    std::cout << "approach1: A->a=" << a->a << std::endl;
}


void approach2( A *pa, B *pb )
{
    printf("approach2: A address: %p B address: %p\n", pa, pb); 

    std::ptrdiff_t offset = reinterpret_cast<char*>(pb) - reinterpret_cast<char*>(pa);
  
    A *a = reinterpret_cast<A*>( reinterpret_cast<char*>(pb) + offset );
    printf("approach2: a address: %p \n", a); 
    std::cout << "approach2: A->a=" << a->a << std::endl;
}

main()
{
  C c;
  std::cout << c.a.a << std::endl;

  approach1(&c.a, &c.b);
  approach2(&c.a, &c.b);
}