There’s a subtlety in C++’s virtual mechanism that I’ve run across twice now. Many other languages don’t have this problem, but C++ is close enough to the bare metal that this is a problem.
In the world of the small, where you have kilobytes of memory to work with, how you program changes dramatically. It’s highly advisable to avoid dynamic memory operations if at all possible. Global variables and class instances are very common.
For a project I wrote a simple, minimal coöperative multitasking core for the ATmega328P, the microcontroller in the Arduino Uno. Being pleased that it worked well enough, I turned the scheduler and process classes into Arduino libraries. The classes were renamed from “Scheduler” and “Process” to “SCMScheduler” and “SCMProcess”, where SCM stands for Simple Coöperative Multitasking.
The intended idea was that each process would descend from SCMProcess, and the scheduler would be initialized with an array of process class instances. The scheduler is very simple and only calls SCMProcess::execute() to pass control over to the next process.
In the original implementation, the scheduler received an array of Process class instances to operate on. (Each process executed identical code, so only one class was necessary.)
[cpp]#define PROCESS_COUNT 10
Scheduler scheduler(processList, PROCESS_COUNT);[/cpp]
The Scheduler’s constructor looked like:
Scheduler(Process* aProcessList, byte aProcessCount) …[/cpp]
There was no class inheritance involved, so the main loop worked without a hitch:
When I abstracted the Process class into a parent (SCMProcess) and a descendent (Process), this broke terribly. (If you’ve had your head in C++ for a long time, you’re probably cringing already.)
The old Scheduler class was removed entirely, and its code lifted to the new generic SCMScheduler class. The constructor was changed to:
SCMScheduler(SCMProcess* aProcessList, byte aProcessCount) …[/cpp]
The process class was split into two pieces, the generic base class and the application-specific class:
[cpp]class Process : public SCMProcess …[/cpp]
The man body of code was only changed to use the new SCMScheduler class.
[cpp]#define PROCESS_COUNT 10
SCMScheduler scheduler(processList, PROCESS_COUNT);[/cpp]
In most other languages, declaring an array of descendents (Process) and passing it to a function that expected an array of parents (SCMProcess*), this is not a problem.
In C++, the symptom I saw was code execution that seemingly wandered though memory at random, resetting the machine over and over. At first it was terribly frustrating until I remembered back to something similar biting me the 1990’s.
For polymorphism to work in C++, you have to pass pointers to the parent class type. You can’t pass references or arrays of class instances. The nitty-gritty details of C++ make this impossible.
The correct constructor for the scheduler is:
SCMScheduler(SCMProcess** aProcessList, byte aProcessCount) …[/cpp]
The main body of code required me to conceded to using the new operator as a balance between machine space constraints and human labour constraints. (Note that I altered the constructor of the Process class for unrelated reasons of convenience.) I may rework the code so that I’m not using new, but for the time being it’s acceptable.
[cpp]#define PROCESS_COUNT 9
SCMProcess* processList[PROCESS_COUNT] =
new Process(13, Catalogue, CATALOGUE_COUNT),
new Process(5, Catalogue, CATALOGUE_COUNT),
new Process(6, Catalogue, CATALOGUE_COUNT),
new Process(7, Catalogue, CATALOGUE_COUNT),
new Process(8, Catalogue, CATALOGUE_COUNT),
new Process(9, Catalogue, CATALOGUE_COUNT),
new Process(10, Catalogue, CATALOGUE_COUNT),
new Process(11, Catalogue, CATALOGUE_COUNT),
new Process(12, Catalogue, CATALOGUE_COUNT)
SCMScheduler scheduler(processList, PROCESS_COUNT);
The design of .NET makes this an interesting discussion.
Microsoft social moderator David M. Morton makes the point
Please don’t use System.Windows.Forms.Application.StartupPath unless you’re actually writing a Windows Forms application. There’s no need to add an unneeded reference to the project. Please use Assembly.GetEntryAssembly to figure out which assembly is the startup assembly in a project that is not a Windows Forms project.
Further discussion on this thread
Assembly.GetExecutingAssembly().Location will return the directory of the executing DLL. You know, good thing you brought this up. I think it would actually be better to use
Instead, because that would give you the first assembly that was executed (the executable) on the off-chance that you call GetExecutingAssembly() from the GAC, you’d get a directory path that would point to the GAC, in case you managed to register your library.
I believe the best option in this case, however (From one of Boban’s posts) might actually be
Although, I’m not completely sure why this is a better option than GetEntryAssembly() would be. I think it may be because of the lack of having to call into Reflection methods, but I personally don’t see any problem with it, as long as it doesn’t kill your performance. I will say that calling the AppDomain version is good if you don’t want to have to pass the string into the Path method for GetDirectoryName.