4

I'm implementing a fancy pointer type which wraps a parallel memory controller, for pointer-like reading to and writing from external SDRAM. The usage requirements are somewhat as following:

// High level overview of class definition:
class extern_pointer {
    class extern_value; // Used as temporary for *e_ptr = ...; etc.

    {...}
};

auto e_ptr = hwstl::extern_pointer<struct_in_external_memory>(...);

// set pointer to point to 0x0010000
// extern_pointer&::operator= (e_ptr::address_type); sets internal ptr value
e_ptr = 0x0010000;

// extern_value&::operator= (e_ptr::char_type);
// note: char_type is the minimum size of addressable set of bits,
// variations for extern_value&::operator= are allowed for wider databusses
*e_ptr = 0x60; 

// invokes const extern_pointer&::operator*,
// obtains a copy and implicitly converts to
// struct_in_external_memory
struct_in_external_memory v = *e_ptr; 

Now as I've been implementing operator* for extern_pointer, I realized operator-> is also a thing.

For operator*, I could just return a temporary extern_value, which overloads some operators to give the pointer-like feeling.
But for -> that isn't the case, since it would require extern_value to contain all the members and methods which refers to the external memory instead.

Is there anything I can do other than deleting extern_value* operator->()?

Edit:

with the declaration of:

class struct_in_external_memory {
    void MyMethod() { /* changes some internal stuff */ }
};

Essentially what I want to be able to do is:

e_ptr->MyMethod()

Normally, MyMethod is a member of struct_in_external_memory, but -> needs to return an 'extern_value*'`

Julian vD
  • 154
  • 9
  • You could use templates. What you describe is in some way similar to how iterators work. – paddy Nov 15 '18 at 12:11
  • What are "all the members and methods which refers to the external memory"? Do you mean members of (and methods on) `struct_in_external_memory`? Or are you providing some kind of proxy? – Useless Nov 15 '18 at 12:11
  • `extern_value` is a proxy for `struct_in_external_memory`. In order to call methods on an `extern_value`, they clearly have to be defined in extern_value. – Julian vD Nov 15 '18 at 12:15
  • But I'm looking for a solution that doesn't require `extern_value` to be a proxy for one specific type, but for any type that can be referenced by an `external_pointer`. While essentially copying all the callable methods and wrapping them with a proxy. – Julian vD Nov 15 '18 at 12:17
  • Why not make your proxy structure inherit the actual structure? Then when the `operator*()` or `operator->()` functions return a reference or pointer, it can return a reference or pointer to the base class `struct_in_external_memory`. – Some programmer dude Nov 15 '18 at 12:20
  • Or make your class not use a proxy structure at all, and instead have an actual instance of the `struct_in_external_memory`? Why do you need a proxy structure? – Some programmer dude Nov 15 '18 at 12:21
  • @Someprogrammerdude The proxy is needed to perform `*e_ptr = 0x60` – Julian vD Nov 15 '18 at 12:28
  • Then I'd say you have conflicting design goals. Either your `extern_pointer` class should behave like a generic everyday pointer, or as a plain pointer to bytes. You can't have both. What would even `*e_ptr = 0x60` mean when the pointed-to data is a structure? What part of the structure should be written to? It just doesn't make sense. – Some programmer dude Nov 15 '18 at 12:33
  • If you need both a generic pointer to anything wrapper, and a pointer to array of bytes, then I suggest you make *two* classes, one for each purpose. That will make the implementation of both much simpler. – Some programmer dude Nov 15 '18 at 12:34

1 Answers1

4

Well, depending on struct_in_external_memory and its complexity, you can still implement it. The last link in the chain of calls to operator-> must be a raw pointer, that is unavoidable, but it doesn't mean it has to be a raw pointer to that actual struct.

class extern_value_holder {
   struct_in_external_memory copy;
public:
  extern_value_holder(/*...*/) {
    // this should initialize copy to reflect the value in external memory
  }
  struct_in_external_memory* operator->() && { return &copy; }

  ~extern_value_holder() {
    //Write copy back to external memory
  }
};

Here the returned pointer is to a copy of the object you want to modify, but cant. The caller modifies it, and it is then synchronized automatically when the extern_value_holder reaches the end of its lifetime at the end of the full expression containing the obj->member.

Which brings us back to what I prefaced this with. It depends on struct_in_external_memory, how cheap it is to copy; on can it even be copied. If you want to support multi-threaded environments then you'll need to add synchronization on top.

Which starts to look like a lot of work for very little benefit. It's just one operator after all.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    The solution you provided did get me somewhere, but as you've mentioned, it's just *one* operator. And supporting a range of structs and performing copies and such may degrade maintainability and performance. I'm currently planning to just omit the -> operator and make it the user's own responsibility to handle structs. – Julian vD Nov 16 '18 at 12:15