0

//I'm moving this note to the top: I'm recreating OOP with structs and associated methods like int MyClass_getInt(MyClass* this)

I'm coding a small DAW in C. I have my Timeline and Region classes in Timeline.h. I would like my UndoRedoStack class to be able to work with multiple Timeline instances so I'd like the UndoRedoStack to be in a separate .c/.h file.

This line of thinking seems to require the TimelineUndoRedoCommand class to know about Timelines and Regions because it needs to backup pre-existing states.

It also requires Timelines to know about TimelineUndoRedoCommands so that it can fire them into the UndoRedoStack.

This seems to be a circular dependency. How should I structure this so that I can avoid circular dependencies?

// I ended up asking Timeline.h to write all the code that needs it but house the UndoRedo separately like so:

/*

    UndoRedoCmd

 */

typedef struct _UndoRedoCmd{
    void* content;
    char* name;
}UndoRedoCmd;

/*

    UndoRedoStack

 */

typedef struct _UndoRedoStack{
    t_LinkList* undoStack;
    t_LinkList* redoStack;
    bool redoingNow;
    void (*redoFunc)(UndoRedoCmd* redoThis);
    void (*undoFunc)(UndoRedoCmd* undoThis);
}UndoRedoStack;

/*
 UndoRedoCmd
 */


UndoRedoCmd* UndoRedoCmd_New(char* name, void* content){
    UndoRedoCmd*  this = malloc(sizeof(UndoRedoCmd));
    this->name=name;
    this->content=content;
    return this;
}


void UndoRedoCmd_Kill(UndoRedoCmd* this){
    /*this should never be used.  instead the
     user of UndoRedoStack should provide
     a custom killer which simply calls
     free after freeing the contents
     */
}


/*
 UndoRedoStack
 */

UndoRedoStack* UndoRedoStack_New(
                                 void (*redoFunc)(UndoRedoCmd*),
                                 void (*undoFunc)(UndoRedoCmd*),
                                 void (*freeLinkFunction)(void*)
                                 ){

    /*
     redoFunc is meant to take the content of the command and redo some action with it.
     undoFunc is the opposite
     freeLinkFunction is meant should free a LinkList_Link with the custom UndoRedo content inside of it.
     */


    UndoRedoStack* this = malloc(sizeof(UndoRedoStack));
    this->undoFunc=undoFunc;
    this->redoFunc=redoFunc;

    this->redoStack = LinkList_New();
    this->redoStack->autoFree=2;
    this->redoStack->customFree=freeLinkFunction;

    this->undoStack = LinkList_New();
    this->undoStack->autoFree=2;
    this->undoStack->customFree=freeLinkFunction;

    return this;


}

void UndoRedoStack_Kill(UndoRedoStack* this){

    LinkList_Free(this->undoStack);
    LinkList_Free(this->redoStack);

    free(this);
}


void UndoRedoStack_do(UndoRedoStack* this,char* name,void* undoredoinfo){
    UndoRedoCmd* mycmd = UndoRedoCmd_New(name, undoredoinfo);
    LinkList_push(this->undoStack, mycmd);

}

void UndoRedoStack_undo(UndoRedoStack* this){
    if(this->undoStack->length==0){
        return;
    }

    UndoRedoCmd* undoMe = (UndoRedoCmd*)LinkList_pop(this->undoStack);
    this->undoFunc(undoMe);
    LinkList_push(this->redoStack, undoMe);
}

void UndoRedoStack_redo(UndoRedoStack* this){

    if(this->redoStack->length==0){
        return;
    }

    UndoRedoCmd* redoMe = (UndoRedoCmd*)LinkList_pop(this->redoStack);
    this->redoFunc(redoMe);
    LinkList_push(this->undoStack, redoMe);
}
Audiomatt
  • 39
  • 5
  • 1
    Classes? C? C++ has classes. C doesn't. – Andrew Henle May 24 '19 at 00:36
  • Use pointers. Forward declare. – Retired Ninja May 24 '19 at 00:42
  • @AndrewHenle , As noted at the bottom, I'm recreating OOP using structs and methods like int MyClass_getInt(MyClass* this); – Audiomatt May 24 '19 at 00:56
  • @TomKarzes I can post my code if necessary, StackOverflow asked specifically if this was a strategic question and I thought rather than post 1000 lines of code, I'd try and describe the problem as succinctly as possible. – Audiomatt May 24 '19 at 00:58
  • @RetiredNinja like just use void* for everything that's shared? I was considering this – Audiomatt May 24 '19 at 00:59
  • If you're going to use void pointers for everything why would you need to forward declare? That is not what I meant at all. – Retired Ninja May 24 '19 at 01:05
  • @RetiredNinja Sorry, I just wasn't sure what the point of saying pointers was when suggesting forward declaration. In any event, I thought about this. Elsewhere on SO, this gets critiqued as sloppy but I'm totally willing to do it. – Audiomatt May 24 '19 at 01:12
  • Possible duplicate of [Resolve circular typedef dependency?](https://stackoverflow.com/questions/888386/resolve-circular-typedef-dependency) – 1201ProgramAlarm May 24 '19 at 04:03
  • Using `void *` for everything removes the limited type safety that C provides for no good reason. You can write `struct Timeline; struct Region;` in the interface (public) header for the `UndoRedoStack` structure (class), and similarly in the other headers. This simply announces that the types exist; there is no information about the size of members of the structure, but there doesn't need to be in the interface (as long as you only pass pointers around). You can then include the implementation (private) headers that define the structure types where needed. – Jonathan Leffler May 24 '19 at 04:46
  • @1201ProgramAlarm was really asking how *not* to have to forward declare strategically. Came up with solution. – Audiomatt May 24 '19 at 14:24
  • @JonathanLeffler Well, I think I solved it using pointers although I *did* use void pointers which, in this context, I think is ok. I'd like to be able to reuse the code. Included code above. – Audiomatt May 24 '19 at 14:32
  • They're a plague (`void *`, that is). However, if you like plagues, by all means use them. You just can't complain when you call functions with the right parameters in the wrong order and the compiler doesn't tell you — you actively prevented it from being able to help you by using `void *` for the types. – Jonathan Leffler May 24 '19 at 14:39
  • I understand completely. I think I'm willing to take that risk for the sake of reuse. To me this seems like a case like data structures. f you're only putting one type in, you'll only ever get one type out. I guess I'm just imagining a template. I understand it's risky but I want other code to use my UndoStack. – Audiomatt May 24 '19 at 15:09

0 Answers0