nr@.Princeton.EDU (Norman Ramsey) (04/10/91)
I wish to create an object that represents a target program suitable for debugging. For simplicity's sake, let me say that the target may be in one of three states: Disconnected: I have symbol table information but no process is executing the target program. Stopped: I am connected to a process that is executing the target program but is currently awaiting instructions from the debugger. Running: I am connected to a process that is executing the target program and is not listening to the debugger. Now, given methods may or may not apply depending on the state. For example, a ``connect'' method applies only to a target in a disconnected state; only a stopped target can be continued, &c. One possible strategy is to use subtyping to distinguish various states, but since targets can change states, many methods would have to return new targets (and somehow invalidate the old ones). Here's an example of that style: TYPE T <: ROOT; Disconnected = T OBJECT METHODS connect(pid:Pid.T):Stopped; END; Stopped = T OBJECT METHODS continue():Running; END; Running = T OBJECT METHODS interrupt():Stopped; END; Worries about this technique: -- clients of the interface must track which subtype they have -- all state-changing methods must invalidate the old object, and all methods must ensure their arguments are valid objects -- what if I can't shoehorn my methods into a single inheritance hierarchy? An alternate technique would be to have a single target type that includes a state: TYPE State = {Disconnected, Stopped, Running}; T <: OBJECT state: State; METHODS connect(pid:Pid.T); (* Disconnected --> Stopped *) continue(); (* Stopped --> Running *) interrupt(); (* Running --> Stopped *) END; Advantages: -- clients need not track which state the target is in, provided they handle exceptions when operations are applied to the wrong state. Troubles: -- all methods must ensure their arguments are in valid states -- specification of what methods do to the states might get complicated I'm leaning toward the second solution, probably just because it's more like non-object-oriented programming, and problems I encounter are likely to be familiar ones. I would appreciate hearing people's comments about this problem, or about experience they've had with similar problems. -- Norman Ramsey nr@princeton.edu
craig@leland.Stanford.EDU (Craig Chambers) (04/10/91)
Dynamic inheritance ala Self would be a convenient mechanism to provide the changing behavior modes of the single debuggee object. The interface to clients would be something much along the lines of your second proposal, but the internal implementation would be more well organized and factored. Since M3 doesn't support dynamic inheritance, you could simulate it with a wrapper object whose single instance variable was changed from one internal state to another; the wrapper object forwards messages to the contained instance variable. -- Craig Chambers