0

I want to provide an subscript operator structure for the struct I am writing. I plan to do this with two structs foo and bar. Code is as follows:

#include <iostream>
struct foo;

struct bar{
    uint32_t *mem;
    uint32_t *opcode;

    bar():mem(nullptr),opcode(nullptr){}
    bar( foo *f, int index ){
        this->mem = f->memory + (index%16);
        this->opcode = f->instructions +(index%16);
    }
    operator bool(){ return (this->mem != nullptr) & (this->opcode != nullptr); }
};
std::ostream& operator<<( std::ostream& os, bar &t ){
    if( t ){
        return os << "bar:" << (*t.mem) << "\t-\t" << (*t.opcode);
    }else{
        return os << "bar: NOT INITIALIZED";
    }
}

struct foo{

    uint32_t *memory = new uint32_t[16]();
    uint32_t *instructions = new uint32_t[16]();

    foo(){}

    ~foo(){
        delete[] this->memory;
        delete[] this->instructions;
    }

   bar &operator[]( int index){
      return bar( *this, index%16 );
   } 

};
std::ostream& operator<<( std::ostream& os, foo &f ){
    for( int i =0 ; i < 16; i++ ){
        os << f.memory[i] << "\t" << f.instructions[i] << "\n";
    }
    return os;
}

I am using CygWin[x86_64] and Notepad++ as main compiler and editor on Windows 7 64 bit.

I have tried a lot of permutations on my own to fix this problem, but I would like to show the following:

bar( foo *f, int index ){
    this->mem = f->memory + (index%16);
    this->opcode = f->instructions +(index%16);
}

and 

bar( foo *f, int index ){
    this->mem = f->memory[index%16];
    this->opcode = f->memory[index%16];
}

'f' is incomplete type error, with note that I have used forward declaration.

bar( foo *f, int index ){
    this->mem = f->memory[index%16];
    this->opcode = f->memory[index%16];
}

two forward declarations notes, and two invalid use of incomplete type struct foo on this->mem = f->memory[index%16] and this->opcode = f->memory[index%16];


I have tried a bunch of other stuff but it seems I have mostly an issue with incomplete type. I have searched SO for answers, and one did explain what is incomplete type, other issue was about recursive definition and this one doesn't define how to make an incomplete type complete.

I am hung on this for past several days, going trough iterations for simple operator overloading. Maybe I am phrasing it wrong in questions, or searching for wrong answers.

But can someone point out my mistakes and/or write how to overload array subscript operator with code and not just body less functions?

Spencer
  • 1,924
  • 15
  • 27
Danilo
  • 1,017
  • 13
  • 32
  • 1
    I made an answer but deleted it because I noticed there are a lot of other things that will break so to get on the right track: Skip the manual memory allocation. `uint32_t *memory = new uint32_t[16];` should be `uint32_t memory[16];` etc. – Ted Lyngmo Nov 06 '19 at 19:45
  • Where'd you put your "terminal" at in the above code. Can you post a more complete sample? – TJ Bandrowsky Nov 06 '19 at 19:46
  • @TJBandrowsky Yeah terminal was remnant of minimal executable example. When I realised that I typed *foo* / *bar* in question I just renamed it. – Danilo Nov 06 '19 at 19:50
  • 1
    @Danilo Already edited that for you. – Spencer Nov 06 '19 at 19:51

2 Answers2

3

Basic design problems (which don't relate to your question) aside, there are two three things that keep this from compiling:

  1. The body of the constructor bar::bar(foo*,int) is defined inline, and uses members from foo. Since foo isn't defined yet (it's an incomplete type), the compiler chokes because it doesn't know about the members of foo yet.

  2. When you call the constructor, you pass in a foo &, not a foo *.

  3. foo::operator[] returns a non-const reference to a temporary, which some compilers might accept, but is just plain wrong. (spotted by Ted Lyngmo)

The following code compiles (https://godbolt.org/z/_F_ZpJ):

#include <iostream>
struct foo;

struct bar
{
    uint32_t *mem;
    uint32_t *opcode;

    bar():mem(nullptr),opcode(nullptr){}
    bar( foo *f, int index );
    bar (bar const &) = default;  // add default copy constructor
    operator bool(){ return (this->mem != nullptr) & (this->opcode != nullptr); }
};


struct foo{

    uint32_t *memory = new uint32_t[16]();
    uint32_t *instructions = new uint32_t[16]();

    foo(){}

    ~foo(){
        delete[] this->memory;
        delete[] this->instructions;
    }

   bar operator[]( int index){
      return bar( this, index%16 ); // this, not *this
   } 

};


bar::bar( foo *f, int index )   // moved the definition down here
{
     this->mem = f->memory + (index%16);
     this->opcode = f->instructions +(index%16);
}

std::ostream& operator<<( std::ostream& os, bar &t )
{
    if( t ){
        return os << "bar:" << (*t.mem) << "\t-\t" << (*t.opcode);
    }else{
        return os << "bar: NOT INITIALIZED";
    }
}


std::ostream& operator<<( std::ostream& os, foo &f )
{
    for( int i =0 ; i < 16; i++ ){
        os << f.memory[i] << "\t" << f.instructions[i] << "\n";
    }
    return os;
}

As I already said, there are some basic design problems and you should think about redesigning this code. Something like the code at this link is better. Better yet, ditch foo altogether use the refactoring suggested in TJ Bandrowsky's answer.

Spencer
  • 1,924
  • 15
  • 27
  • Would you mind answering why did you choose to put constructor bellow the `foo` struct? – Danilo Nov 06 '19 at 19:48
  • 1
    `bar &operator[]( int index)` was one of the things I commented in my now deleted answer. It compiles and will probably end in disaster. Edit: Actually, it doesn't compile: https://godbolt.org/z/oIJ5U2 – Ted Lyngmo Nov 06 '19 at 19:51
  • @Danilo As I said in the post, `foo` is an incomplete type, You can't refer to members of `foo` until after the struct is defined. – Spencer Nov 06 '19 at 19:52
  • @TedLyngmo Agreed. – Spencer Nov 06 '19 at 19:53
  • @Spencer Yes, sometimes it needs some help: https://godbolt.org/z/vQiNGL (or in this case, silly much help. `/W4` is an almost unusable warning level). It still compiles with MSVC, but at least with a warning. – Ted Lyngmo Nov 06 '19 at 20:01
  • @Spencer, would you mind adding the assigment operator overloading since it fails for me ? – Danilo Nov 06 '19 at 20:03
  • @Danilo I added a default copy constructor, which is necessary for this to work. You shouldn't need to add any assignment operator. – Spencer Nov 06 '19 at 20:10
  • @Spencer, why i shouldn't need any assignment operator ? There is implied way to change the values of memory and opcode of bar? Also I've run this code, and it prints foo perfectly, but for bar `std::cout` returns 1. This has happened before for me when I tried to print uninitialized structure. Maybe my compiler has gone mental. – Danilo Nov 06 '19 at 20:19
  • @Danilo The way you've designed it, you can't assign them separately. And the assignment is ill-defined -- Do you want to assign the pointers, or what's behind the pointers? `std::cout` doesn't return anything -- it's an object. What makes you think it returns 1? – Spencer Nov 06 '19 at 20:26
  • 1
    @Danilo If you are going to continue down this rabbit hole, at least read [What is The Rule of Three?](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) and [The rule of three/five/zero](https://en.cppreference.com/w/cpp/language/rule_of_three). – Ted Lyngmo Nov 06 '19 at 20:28
  • In example you provided (from original code) ostream operators are overloaded. Which means that foo and bar are printable. Looks like there is serious language barrier from my side @Spencer. I don't know how to parse it differently. Ostream operators are overloaded, both objects are printable. When I try to print `f[4]` for example it returns 1, but it should return values stored at index 4. – Danilo Nov 06 '19 at 20:30
0

I'd consider refactoring the thing to something like:

bar {
uint opcode, mem;
}

then make your machine model a std:vector and it would be a lot simpler.

TJ Bandrowsky
  • 842
  • 8
  • 12
  • What is machine model? I have tried using `std::vector` ... I had bad time referencing it since (and I've read this on SO somewhere) it can not be changed. – Danilo Nov 06 '19 at 19:49
  • Well, it looks like you are trying to write something that emulates assembler, so a list of your bars would be like a program. Therefor std::vector instructions. Then you just loop through instructions ...the tell tale sign is that you are using way too many pointers for modern C++. Instead of trying to roll your own collections, use the ones they have. – TJ Bandrowsky Nov 06 '19 at 19:54
  • @Danilo What do you mean by "it cannot be changed"? You can insert, delete, and update individual values of a `vector`.... – Spencer Nov 06 '19 at 19:55
  • @Spencer. I tried to implement subscirpt operator with 1D `std::vector` instead of 2 `array`s but, when I searched for the issue I was having (on some questions several days ago) it said that reference to item in vector cant change the value of vectors item. – Danilo Nov 06 '19 at 19:59
  • @TJBandrowsky yeah, assembler is close to what I want to do, but every memory is tied to instruction, no looping over programs - just bouncing out variables between memories and their instructions. That is why I need to hold in bar opcode and memory, since I can then use operators `>>`, `<<` and `=` to set,get memory and assign instruction. – Danilo Nov 06 '19 at 20:05
  • @Danilo You shouldn't need to implement `operator[]`. `vector` already does that for you. – Spencer Nov 06 '19 at 20:06
  • So I do have 1 vector that holds entire file, but now I need to reference specific memory segments that are changeable, so if I grab 1 instruction, and its memory with offset of 16*4 bytes, that is what I need to access with subscript operator overloading. It seems much easier than running over whole file every time I need to access memory or instruction. – Danilo Nov 06 '19 at 20:14
  • In that case what you do is change bar to like so: class bar { int opcode; std::vector mem; } class machine { std::vector instructions; } Now you have variable length memory, can access each instruction individually, and C++ does all the heavy lifting for you. – TJ Bandrowsky Nov 06 '19 at 20:41