Thread: [Audacity-devel] Prefs sample format/rate in NewWaveTrack
A free multi-track audio editor and recorder
Brought to you by:
aosiniao
From: Otto W. <ott...@bl...> - 2002-11-17 10:04:11
|
I've discovered that neither "NewWaveTrack" nor "new WaveTrack" handles the prefs correct, depending on the occurrence always different hard coded values are uses. 1. Why is there a "NewWaveTrack" if still "new WaveTrack" is used? 2. Should "WaveTrack" have parameters for "format" and "rate" or not and should it enforce it (no defaults)? Or should "WaveTrack" simply use the prefs values? The best solution IMO is WaveTrack has parameters with defaults where these defaults where taken from the prefs. All occurrence of "WaveTrack" have to be checked if they should remove their initial values. If my suggestion is chosen, I'd like to know where the prefs should be read? They should be used in "WaveTrack.h" but I don't know if this is a good idea. Maybe it should be done with overloaded functions. O. Wyss |
From: Joshua H. <jo...@ha...> - 2002-11-17 20:16:29
|
Otto Wyss <ott...@bl...> wrote: > I've discovered that neither "NewWaveTrack" nor "new WaveTrack" handles the > prefs correct, depending on the occurrence always different hard coded values > are uses. > > 1. Why is there a "NewWaveTrack" if still "new WaveTrack" is used? We are moving toward using only NewWaveTrack. Parts of the code that use "new WaveTrack" just have not been converted yet. TrackFactory and NewWaveTrack were created as part of the process of splitting the backend code (libaudacity) from the GUI code. Once the split gets farther along, there will be a class GUIWaveTrack (or something like that) that is derived from WaveTrack. All the tracks the program uses will be GUIWaveTracks, but through the magic of inheritance, the libaudacity code will be able to use them as if they were plain WaveTracks. When new tracks are created they need to be GUIWaveTracks, but the backend code doesn't know anything about a GUIWaveTrack. So what will happen is the GUI code will pass the backend code a TrackFactory that is capable of creating WaveTracks. Unbeknownst to the backend code, these new WaveTracks will actually be GUIWaveTracks. > 2. Should "WaveTrack" have parameters for "format" and "rate" or not and should > it enforce it (no defaults)? Or should "WaveTrack" simply use the prefs values? For the sample rate, I believe it's intended to be a default rate for the *project*, not individual tracks. This seems to make the most sense, because creating tracks with a different sampling rate than the project is setting yourself up for needing resampling, which Audacity can't even do right now. As far as the format goes, it seems like there are only a few situations where using the preference would make sense. Whenever you're importing a file, for example, you want to create a track of the same sample format that the decoder spits at you; otherwise you're doing an extra conversion for no reason, and losing information in the process. > The best solution IMO is WaveTrack has parameters with defaults where these > defaults where taken from the prefs. All occurrence of "WaveTrack" have to be > checked if they should remove their initial values. I think that's fine, except do this to TrackFactory::NewWaveTrack() instead of the actual WaveTrack constructor, since the goal is to remove all occurences of "new WaveTrack" with the exception of the one that is in the body of TrackFactory::NewWaveTrack() itself. > If my suggestion is chosen, I'd like to know where the prefs should be read? > They should be used in "WaveTrack.h" but I don't know if this is a good idea. > Maybe it should be done with overloaded functions. Make the default argument '0' (or something). Then in the body of TrackFactory::NewWaveTrack(), read and use the preference if the argument is 0. This reminds me of an easy side project that anyone could take on. Right now gPrefs is a global wxConfig object, that lets you read any arbitrary prefs path (like "/FileFormats/ExportFormat_SF1"). There are snippits of code throughout the program that read and write to the preferences. What would be nice is if the wxConfig object were not exposed globally. What you could do is create a Preferences object that had the wxConfig object as a member. Then create a method for each preference that needs to be read or written. This has been done already with the functions ReadExportFormatPref() and WriteExportFormatPref(). Here are the advantages: 1. there is centralized documentation that lists all the preferences Audacity ever needs to read or write. I attempted to document all the preferences Audacity uses at the top of Prefs.cpp a long time ago, and as you can see it's fallen terribly out of date. 2. There is no longer any worry of misspelling or miswriting the name of a particular preference, because the compiler will flag it with a "no such method" error. 3. it's easy to find what the default is for some particular preference. Right now these are sprinkled throughout the program, and may actually be different in different places! Josh -- "Only Angels can sing that high, and only dogs can hear it." -- James Evans |
From: Otto W. <ott...@bl...> - 2002-11-17 20:53:00
|
Thanks for the detailed description. > > > 1. Why is there a "NewWaveTrack" if still "new WaveTrack" is used? > > We are moving toward using only NewWaveTrack. Parts of the code that use "new > WaveTrack" just have not been converted yet. > > TrackFactory and NewWaveTrack were created as part of the process of splitting > the backend code (libaudacity) from the GUI code. Once the split gets farther > I'm definitely are interested in this factory programming, I'll have a close look how it's done. Could you also give me any pointer to tutorials or documentation? > > The best solution IMO is WaveTrack has parameters with defaults where these > > defaults where taken from the prefs. All occurrence of "WaveTrack" have to be > > checked if they should remove their initial values. > > I think that's fine, except do this to TrackFactory::NewWaveTrack() instead of > the actual WaveTrack constructor, since the goal is to remove all occurences of > "new WaveTrack" with the exception of the one that is in the body of > TrackFactory::NewWaveTrack() itself. > Okay. > > If my suggestion is chosen, I'd like to know where the prefs should be read? > > They should be used in "WaveTrack.h" but I don't know if this is a good idea. > > Maybe it should be done with overloaded functions. > > Make the default argument '0' (or something). Then in the body of > TrackFactory::NewWaveTrack(), read and use the preference if the argument is 0. > That was my intention. SampleRate = 0 is no problem. SampleFormat is an enumeration but casting to 0 shouldn't be either. I won't look at each occurrence of "new WaveTrack" since I don't know when parameters or prefs are needed. I'll stick to the menu command and the recording. > This reminds me of an easy side project that anyone could > take on. Right now gPrefs is a global wxConfig object, that lets you read > any arbitrary prefs path (like "/FileFormats/ExportFormat_SF1"). There are > snippits of code throughout the program that read and write to the preferences. > > What would be nice is if the wxConfig object were not exposed globally. What > you could do is create a Preferences object that had the wxConfig object as a > member. Then create a method for each preference that needs to be read or > written. This has been done already with the functions ReadExportFormatPref() > and WriteExportFormatPref(). > I may have a look when the NewWaveTrack is working unless someone else wants to do it. O. Wyss |
From: Otto W. <ott...@bl...> - 2002-11-22 21:21:15
|
> What would be nice is if the wxConfig object were not exposed globally. What > you could do is create a Preferences object that had the wxConfig object as a > member. Then create a method for each preference that needs to be read or > written. This has been done already with the functions ReadExportFormatPref() > and WriteExportFormatPref(). > Hiding wxConfig is a good idea but why not simple varibles instead of methods? O. Wyss |
From: Matt B. <mbr...@cs...> - 2002-11-22 22:20:05
|
> What would be nice is if the wxConfig object were not exposed > globally. What you could do is create a Preferences object that had > the wxConfig object as a member. Then create a method for each > preference that needs to be read or written. I disagree. This wouldn't gain us any simplicity for code that uses preferences, but it would add an extra step of updating the "middleman" object whenever preferences are added or changed. We'd still need a global object (or an effectively global object, owned by the app and exposed to all prefs clients), and we'd just be replacing wxConfig calls with functionally identical wrappers everywhere. We would gain a little extra compile-time checking (typos in the preference names would be limited to inside the wrapper object), but I don't think it's worth the hassle, since using the names directly has never been a problem. The "Preferences" object would effectively depend on every part of the code with configurable settings, and all of that code would depend on the Preferences object. This would create huge interdependencies through the whole project: Adding a preference to the Noise Removal effect would change the interface to the Preference wrapper, causing virtually all Audacity code to need recompilation. |
From: Otto W. <ott...@bl...> - 2002-11-23 06:15:25
|
> > What would be nice is if the wxConfig object were not exposed > > globally. What you could do is create a Preferences object that had > > the wxConfig object as a member. Then create a method for each > > preference that needs to be read or written. > > I disagree. This wouldn't gain us any simplicity for code that uses > preferences, but it would add an extra step of updating the "middleman" > Just check how many different location were used for sample rate and format. The prefs are changed at least once if not twice and not all occurrence were corrected. O. Wyss |
From: Matt B. <mbr...@cs...> - 2002-11-23 16:01:22
|
On Nov 23, Otto Wyss wrote: > > I disagree. This wouldn't gain us any simplicity for code that uses > > preferences, but it would add an extra step of updating the "middleman" > > Just check how many different location were used for sample rate and > format. The prefs are changed at least once if not twice and not all > occurrence were corrected. True. But we would still have needed to update all occurences even with a wrapper function in use, because the semantics of the preference changed. Instead of changing our calls to gPrefs->Read, we would need to change all our calls to Preferences::GetDefaultSampleFormat. |
From: Dominic M. <do...@mi...> - 2002-11-18 07:38:54
|
Now that 1.1.1 has been released (albeit with one hiccup on Windows), this is a great time to think about doing some structural changes like this one. I'd like to add a gain control to every track, and I think it would be a lot easier after this reorganization. > TrackFactory and NewWaveTrack were created as part of the process of splitting > the backend code (libaudacity) from the GUI code. Once the split gets farther > along, there will be a class GUIWaveTrack (or something like that) that is > derived from WaveTrack. All the tracks the program uses will be GUIWaveTracks, > but through the magic of inheritance, the libaudacity code will be able to use > them as if they were plain WaveTracks. > > When new tracks are created they need to be GUIWaveTracks, but the backend code > doesn't know anything about a GUIWaveTrack. So what will happen is the GUI > code will pass the backend code a TrackFactory that is capable of creating > WaveTracks. Unbeknownst to the backend code, these new WaveTracks will > actually be GUIWaveTracks. Josh, I can't remember: did we figure out a way to do this? When I try to work out the details now, it doesn't work. Here are the properties we would like to achieve: * The WaveTrack class is fully functional but has no GUI, and in fact no member variables that have to do with GUI-specific functions. (This is almost true now - it only has a couple of member variables. Everything else is in TrackArtist.) * WaveTrack inherits from Track, so that other tracks like LabelTracks can share the same interface for editing operations like Cut, Copy, and Paste. * GUIWaveTrack inherits from WaveTrack, so that an object (like an Effect) that wants to create a WaveTrack can do so using a TrackFactory, and never know that it's really a GUIWaveTrack. * GUIWaveTrack inherits from GUITrack, so that methods common to all track GUIs are inherited. (Drawing/handling the track's label, for example.) Clearly this wouldn't work. The inheritance diagram would look something like this: Track /\ / \ / \ / GUITrack / | WaveTrack | \ | \ | GUIWaveTrack ...which doesn't work, because GUIWaveTrack inherits from Track twice. Here are some possible solutions: * GUITrack doesn't inherit from Track, but instead contains a pure virtual method GetTrack(), which GUIWaveTrack would implement to return itself (since GUIWaveTrack is a Track). Warning: this still uses multiple inheritance!!! * GUITrack doesn't inherit from Track, but instead contains a Track. This also means that all GUI functions would have to use a GetTrack() accessor. Any thoughts? I can't seem to come up with any way for GUIWaveTrack to inherit from all three without causing problems. - Dominic |
From: Joshua H. <jo...@ha...> - 2002-11-19 05:12:09
|
Dominic Mazzoni <do...@mi...> wrote: > Now that 1.1.1 has been released (albeit with one hiccup on Windows), > this is a great time to think about doing some structural changes like > this one. Cool! > > TrackFactory and NewWaveTrack were created as part of the process of splitting > > the backend code (libaudacity) from the GUI code. Once the split gets farther > > along, there will be a class GUIWaveTrack (or something like that) that is > > derived from WaveTrack. All the tracks the program uses will be GUIWaveTracks, > > but through the magic of inheritance, the libaudacity code will be able to use > > them as if they were plain WaveTracks. > > > > When new tracks are created they need to be GUIWaveTracks, but the backend code > > doesn't know anything about a GUIWaveTrack. So what will happen is the GUI > > code will pass the backend code a TrackFactory that is capable of creating > > WaveTracks. Unbeknownst to the backend code, these new WaveTracks will > > actually be GUIWaveTracks. > > Josh, I can't remember: did we figure out a way to do this? When I try > to work out the details now, it doesn't work. I remembered that we came across a problem like this, but I couldn't remember exactly what it was. > Here are the properties we would like to achieve: > > * The WaveTrack class is fully functional but has no GUI, and in fact > no member variables that have to do with GUI-specific functions. > (This is almost true now - it only has a couple of member variables. > Everything else is in TrackArtist.) > > * WaveTrack inherits from Track, so that other tracks like LabelTracks > can share the same interface for editing operations like Cut, Copy, > and Paste. > > * GUIWaveTrack inherits from WaveTrack, so that an object (like an > Effect) that wants to create a WaveTrack can do so using > a TrackFactory, and never know that it's really a GUIWaveTrack. > > * GUIWaveTrack inherits from GUITrack, so that methods common to > all track GUIs are inherited. (Drawing/handling the track's > label, for example.) > > Clearly this wouldn't work. The inheritance diagram would look > something like this: > > Track > /\ > / \ > / \ > / GUITrack > / | > WaveTrack | > \ | > \ | > GUIWaveTrack > > ...which doesn't work, because GUIWaveTrack inherits from Track twice. Actually, I just pulled Stroustrup off my shelf and it covers this very thing. Using virtual inheritance we could indeed design the hierarchy this way. I need to take more time to read this to fully understand the implications, but I think it would be a perfect fit for our situation. If you have Stroustrup (_The C++ Programming Language_), check out 15.2.4 and 15.2.5. I suppose we'd need to make sure that our target compilers support virtual inheritance satisfactorily. (People knock C++ all the time, but the fact that it can accommodate a situation like this makes me like it even more). > Here are some possible solutions: > > * GUITrack doesn't inherit from Track, but instead contains a > pure virtual method GetTrack(), which GUIWaveTrack would implement > to return itself (since GUIWaveTrack is a Track). > > Warning: this still uses multiple inheritance!!! > > * GUITrack doesn't inherit from Track, but instead contains a Track. > This also means that all GUI functions would have to use a GetTrack() > accessor. Either of these would work fine, but aren't quite as nice. :-) Josh -- "If we ever meet up with aliens from some other world, they will probably use the equivalent of radians, too." -- Eugene Hecht, _Physics: Calculus_ |
From: Dominic M. <do...@mi...> - 2002-11-19 09:01:49
|
>> Clearly this wouldn't work. The inheritance diagram would look >> something like this: >> >> Track >> /\ >> / \ >> / \ >> / GUITrack >> / | >> WaveTrack | >> \ | >> \ | >> GUIWaveTrack >> >> ...which doesn't work, because GUIWaveTrack inherits from Track twice. > > Actually, I just pulled Stroustrup off my shelf and it covers this very > thing. Using virtual inheritance we could indeed design the hierarchy > this > way. I need to take more time to read this to fully understand the > implications, but I think it would be a perfect fit for our situation. > If you have Stroustrup (_The C++ Programming Language_), check out > 15.2.4 and 15.2.5. > > I suppose we'd need to make sure that our target compilers support > virtual inheritance satisfactorily. > > (People knock C++ all the time, but the fact that it can accommodate > a situation like this makes me like it even more). Hmmmm, is the idea that Track would be an "Interface" (in the Java sense)? I don't have Stroustrup handy but I'll look it up tomorrow. - Dominic |
From: Joshua H. <jo...@ha...> - 2002-11-19 21:02:45
|
Dominic Mazzoni <do...@mi...> wrote: > >> Clearly this wouldn't work. The inheritance diagram would look > >> something like this: > >> > >> Track > >> /\ > >> / \ > >> / \ > >> / GUITrack > >> / | > >> WaveTrack | > >> \ | > >> \ | > >> GUIWaveTrack > >> > >> ...which doesn't work, because GUIWaveTrack inherits from Track twice. > > > > Actually, I just pulled Stroustrup off my shelf and it covers this very > > thing. Using virtual inheritance we could indeed design the hierarchy > > this > > way. I need to take more time to read this to fully understand the > > implications, but I think it would be a perfect fit for our situation. > > If you have Stroustrup (_The C++ Programming Language_), check out > > 15.2.4 and 15.2.5. > > > > I suppose we'd need to make sure that our target compilers support > > virtual inheritance satisfactorily. > > > > (People knock C++ all the time, but the fact that it can accommodate > > a situation like this makes me like it even more). > > Hmmmm, is the idea that Track would be an "Interface" (in the > Java sense)? No, these are pretty different ideas. Java's interfaces are a way to get most of the benefits of multiple inheritance without actually supporting multiple inheritance. A Java class can inherit from only one class, but from as many interfaces as you want (where an interface is essentially a class where everything must be purely abstract). This way there is only one implementation inheritance path, and no risk of implementation conflicts or ambiguity. This virtual inheritance that C++ supports is a way of having different classes inherit the same copy of a base class that has implementation. Using virtual inheritance, both WaveTrack and GUITrack above would inherit the same copy of Track's members. That way GUIWaveTrack would only have one copy of Track, not two. BeOS used virtual inheritance in their media system. BMediaNode [0] defines and implements a protocol for exchanging data in the media system. BBufferConsumer and BBufferProducer both inherit from BMediaNode virtually, so that a custom media node can inherit from both BBufferConsumer and BBufferProducer, but still be only a single media node. They include a note about virtual inheritance at the bottom of [1], dealing with how constructors must be called in this situation. The fact that this scheme was used in the BeOS API gives me a lot of confidence that it could be appropriate in our situation. My only concern is that we be able to instantize WaveTrack without having to derive from it. This issue didn't show up in the BeOS situation, because it doesn't make sense to instantize BBufferConsumer. Josh [0] http://bang.dhs.org/be/bebook/The%20Media%20Kit/MediaNode.html [1] http://bang.dhs.org/be/bebook/The%20Media%20Kit/Intro.html -- "If we ever meet up with aliens from some other world, they will probably use the equivalent of radians, too." -- Eugene Hecht, _Physics: Calculus_ |
From: Dominic M. <do...@mi...> - 2002-11-19 22:22:55
|
> >>> Track > >>> /\ > >>> / \ > >>> / \ > >>> / GUITrack > >>> / | > >>> WaveTrack | > >>> \ | > >>> \ | > >>> GUIWaveTrack > >>> > This virtual inheritance that C++ supports is a way of having different > classes inherit the same copy of a base class that has implementation. > Using virtual inheritance, both WaveTrack and GUITrack above would > inherit the same copy of Track's members. That way GUIWaveTrack would > only have one copy of Track, not two. Wow, I never knew this was possible! Does this look right? class Track { } class WaveTrack: public virtual Track { } class GUITrack: public virtual Track { } class GUIWaveTrack: public GUITrack, public WaveTrack { // constructor GUIWaveTrack(): Track(), GUITrack(), WaveTrack() { } } It's not entirely clear to me if we're allowed to instantiate a WaveTrack if we do this. I hope so. - Dominic |
From: Shane M. <smu...@um...> - 2002-11-19 22:53:21
|
According to Day 13 of "Teach yourself C++ in 21 days" (I actually made it to Day 15), you can do class Animal{ //stuff }; class Bird: virtual public Animal { //stuff }; class Horse: virtual public Animal { //stuff }; class Pegasus: public Horse, public Bird { //stuff }; and the Pegasus constructor looks like: Pegasus::Pegasus(int color, int height, int age, int migrates): Horse(color, height, age), Bird (color, migrates, age), Animal(Age) { }; This allows transparent operation without requiring you to disambiguate which parent method you care about (as would be necessary if you didn't use virtual children). But if horse and bird have identical methods that are not shared by animal (e.g., GetColor()), you need to specify which one you mean in Pegasus. So, something like Pegasus::GetColor(){return Horse::GetColor()};. I could imagine this type of thing happening from time to time, and so there maybe should be clear comments somewhere as a reminder. The only real problems I can forsee with this is RTTI issues which are not handled so elegantly by wx, if I remember right. Stm... On Tue, 2002-11-19 at 17:22, Dominic Mazzoni wrote: > > >>> Track > > >>> /\ > > >>> / \ > > >>> / \ > > >>> / GUITrack > > >>> / | > > >>> WaveTrack | > > >>> \ | > > >>> \ | > > >>> GUIWaveTrack > > >>> > > > This virtual inheritance that C++ supports is a way of having different > > classes inherit the same copy of a base class that has implementation. > > Using virtual inheritance, both WaveTrack and GUITrack above would > > inherit the same copy of Track's members. That way GUIWaveTrack would > > only have one copy of Track, not two. > > Wow, I never knew this was possible! > > Does this look right? > > class Track { > } > > class WaveTrack: public virtual Track { > } > > class GUITrack: public virtual Track { > } > > class GUIWaveTrack: public GUITrack, public WaveTrack { > // constructor > GUIWaveTrack(): Track(), GUITrack(), WaveTrack() { > } > } > > It's not entirely clear to me if we're allowed to instantiate > a WaveTrack if we do this. I hope so. > > - Dominic > > > > ------------------------------------------------------- > This sf.net email is sponsored by: To learn the basics of securing > your web site with SSL, click here to get a FREE TRIAL of a Thawte > Server Certificate: http://www.gothawte.com/rd524.html > _______________________________________________ > Audacity-devel mailing list > Aud...@li... > https://lists.sourceforge.net/lists/listinfo/audacity-devel -- Shane Mueller <smu...@um...> |
From: Joshua H. <jo...@ha...> - 2002-11-20 03:41:36
|
Shane Mueller <smu...@um...> wrote: > According to Day 13 of "Teach yourself C++ in 21 days" (I actually made > it to Day 15), > > you can do > > class Animal{ > //stuff > }; > class Bird: virtual public Animal { > //stuff > }; > class Horse: virtual public Animal { > //stuff > }; > class Pegasus: public Horse, public Bird { > //stuff > }; > > and the Pegasus constructor looks like: > > Pegasus::Pegasus(int color, int height, int age, int migrates): > Horse(color, height, age), > Bird (color, migrates, age), > Animal(Age) > { > }; > > This allows transparent operation without requiring you to disambiguate > which parent method you care about (as would be necessary if you didn't > use virtual children). But if horse and bird have identical methods > that are not shared by animal (e.g., GetColor()), you need to specify > which one you mean in Pegasus. I think you're missing the point here. Virtual inheritance doesn't have anything to do with code (methods), it has to do with data. Let's take your pegasus example, slightly modified: class Animal{ public: Animal() { } int age; }; class Bird: public Animal { public: Bird() { } }; class Horse: public Animal { public: Horse() { } }; class Pegasus: public Horse, public Bird { Pegasus(); }; Pegasus::Pegasus() { age = 1; }; Compiling this with g++ gives me the error: test.cpp: In method `Pegasus::Pegasus()': test.cpp:24: request for member `age' is ambiguous in multiple inheritance lattice test.cpp:5: candidates are: int Animal::age test.cpp:5: int Animal::age Pegasus::Pegasus() cannot be compiled, because reference to "age" is ambiguous. The Pegasus object contains two copies of the Animal object; the one that came with Bird and the one that came with Horse, and each one has its own copy of "age." The way to solve this is to have Bird and Horse virtually derive from Animal, which causes Bird and Horse to contain the same copy of Animal, and thus Animal::age. This causes the example to compile correctly. The same issue can be manifested with methods: class Animal{ public: Animal() { } void SetAge(int the_age) { age = the_age; } private: int age; }; class Bird: public Animal { public: Bird() { } }; class Horse: public Animal { public: Horse() { } }; class Pegasus: public Horse, public Bird { Pegasus(); }; Pegasus::Pegasus() { SetAge(1); }; Compiling this gives: test.cpp: In method `Pegasus::Pegasus()': test.cpp:26: request for member `SetAge' is ambiguous in multiple inheritance lattice test.cpp:5: candidates are: void Animal::SetAge(int) test.cpp:5: void Animal::SetAge(int) This looks like it's an ambiguous method call, but it's not; it's an ambiguous reference to data. It's clear what code is going to be called: Animal::SetAge(). What is ambiguous is which copy of Animal will be supplied as the implicit *this pointer. Again, making the derivation from Animal virtual solves this problem and makes the example compile correctly. Moving back to the WaveTrack/GUIWaveTrack example, we want GUIWaveTrack to have only a single copy of Track, so there is only a single copy of members like mMute, mSolo, and mOffset. Methods in Track, WaveTrack, GUITrack, and GUIWaveTrack could each refer to a member like mMute and they will all get the same variable. This is what we want. Now as far as methods go, let's take an example like Cut(). Track::Cut() does nothing, because there's nothing generic you can do in response to a cut. WaveTrack::Cut() will alter the data structures holding the WaveTrack to make the cut happen properly. GUITrack::Cut() will perform the operations that the GUI takes in response to a cut; redraw the screen at least. GUIWaveTrack::Cut() would call WaveTrack::Cut() and then GUITrack::Cut(), and could also do anything that doesn't fit into GUITrack or WaveTrack. Many of the methods in GUIWaveTrack would probably be compositions of the corresponding methods from WaveTrack and GUITrack. The more I think about this the more convinced I am that this is the right solution to the problem. The concern I voiced earlier about perhaps not being able to instantiate WaveTrack can be worked around. Here is the issue: since all classes that derive from a class virtually share the same copy of that class, their constructors do not automatically call the base class's constructor as would normally happen with inheritance. If all of them called the constructor, the constructor would be called many times on the same object, which could be bad. However, we can put a manual call to Track::Track() inside WaveTrack so we can instantiate WaveTrack. Instantiating GUIWaveTrack can then rely on WaveTrack to call Track::Track(), so there is no problem. The problem would come only if both of these were true: * it was harmful to call Track::Track() more than once. in our case it's not; Track::Track() only sets some default values for its members. * we had to write WaveTrack without knowing what other classes it would be inherited with. It if was inherited along with another class which also virtually inherited from Track and *also* called Track::Track() from its constructor, Track::Track() would be called twice. Since we have neither of these constraints, we will be fine. > The only real problems I can forsee with this is RTTI issues which are > not handled so elegantly by wx, if I remember right. It's not clear to me what RTTI has to do with this, or wx. RTTI is not involved until you try to dynamic_cast something, which we are not doing. Josh -- "If we ever meet up with aliens from some other world, they will probably use the equivalent of radians, too." -- Eugene Hecht, _Physics: Calculus_ |
From: Joshua H. <jo...@ha...> - 2002-11-20 07:39:11
|
Joshua Haberman <jo...@ha...> wrote: > The concern I voiced earlier about > perhaps not being able to instantiate WaveTrack can be worked around. > Here is the issue: since all classes that derive from a class virtually > share the same copy of that class, their constructors do not > automatically call the base class's constructor as would normally happen > with inheritance. If all of them called the constructor, the > constructor would be called many times on the same object, which could > be bad. However, we can put a manual call to Track::Track() inside > WaveTrack so we can instantiate WaveTrack. Instantiating GUIWaveTrack > can then rely on WaveTrack to call Track::Track(), so there is no > problem. The problem would come only if both of these were true: > > * it was harmful to call Track::Track() more than once. in our > case it's not; Track::Track() only sets some default values for > its members. > * we had to write WaveTrack without knowing what other classes > it would be inherited with. It if was inherited along with > another class which also virtually inherited from Track and > *also* called Track::Track() from its constructor, Track::Track() > would be called twice. > > Since we have neither of these constraints, we will be fine. Actually, I was wrong about that. It's even easier. C++ automatically ensures that the base class's constructor only gets called once. It eliminates any calls (implicit or explicit) to the base class's constructor in all but the most derived class: #include <iostream> class Base { public: Base(const char *message) { std::cout << message; } }; class Mid1 : public virtual Base { public: Mid1():Base("Mid1 called Base ctor\n") { std::cout << "Mid1 ctor called\n"; } }; class Mid2 : public virtual Base { public: Mid2():Base("Mid2 called Base ctor\n") { std::cout << "Mid2 ctor called\n"; } }; class Derived : public Mid1, public Mid2 { public: Derived():Base("Derived called Base ctor\n") { std::cout << "Derived ctor called\n"; } }; int main() { std::cout << "Instantiating Mid1\n"; Mid1 mid1; std::cout << "\nInstantiating Derived\n"; Derived derived; } This prints out: Instantiating Mid1 Mid1 called Base ctor Mid1 ctor called Instantiating Derived Derived called Base ctor Mid1 ctor called Mid2 ctor called Derived ctor called Notice that both Mid1 and Mid2 have calls to the Base::Base(). You can see this in effect when Mid1 is instantiated: that's how you get the message "Mid1 called base ctor." But when you instantiate Derived, neither of the Mid*'s constructors call Base::Base(). C++ simply eliminates them to prevent the problem of Base::Base() being called more than once. Yay C++! Sorry if I'm overloading you with information, I just felt a spark on this one and ran with it. Josh -- "If we ever meet up with aliens from some other world, they will probably use the equivalent of radians, too." -- Eugene Hecht, _Physics: Calculus_ |
From: Dominic M. <do...@mi...> - 2002-11-20 17:43:06
|
Joshua Haberman wrote: > Sorry if I'm overloading you with information, I just felt a spark on > this one and ran with it. > Au contraire, thank you for the detailed discussion. I'm feeling very happy about the new hierarchy now, and I can't wait to start coding it! - Dominic |