A Small DetourIn this article, I will be taking a break from developing the runtime component of the scripting system in order to deal with more pressing issues. Although I would have liked for the issues dealt with in this article to be minor enough to allow further development afterwards, they take quite a bit of explanation. This article is a bit larger than the last two without any additional development, and would grow to be monstrous with anything else attached. For this reason, I have opted to set these issues apart, and continue developing the virtual machine next time. Cleaning UpThe first thing to do is to fix a little problem from last time. The Instruction class did not support proper copy-on-write, which caused problems for some, while not even being known to me. It can be corrected rather easily, as I had shown in the forum discussion for the second article: // the basic instruction class Instruction { public: Instruction(opcode code) : _code(code), _datsize(0), _data(0) {} Instruction(opcode code, const char* data, size_t dataSize) : _code(code), _datsize(dataSize), _data(new char[dataSize]) { memcpy(_data, data, dataSize); } // we must store our own internal copy Instruction(const Instruction& instr) : _code(instr._code), _datsize(instr._datsize), _data(new char[_datsize]) { memcpy(_data, instr._data, _datsize); } void operator=(const Instruction& instr) { delete[] _data; _size = instr._size; _data = new char[_size]; memcpy(_data, instr._data, _datsize); } ~Instruction() { delete[] _data; } // and we must then clean up after it opcode Code() const { return _code; } const char* Data() const { return _data; } private: opcode _code; size_t _datsize; char* _data; // additional data }; One thing you should notice is that these additions now make up just about 50% of the Instruction class. It's bloated. We should improve this situation by separating the copy-on-write functionality from Instruction. We do this by creating a simple Buffer class that will encapsulate the data. // data buffer to encapsulate copy-on-write functionality class Buffer { public: Buffer() : _size(0), _data(0) {} Buffer(const char* data, size_t size) : _size(size), _data(new char[_size]) { memcpy(_data, data, _size); } Buffer(const Buffer& buf) : _size(buf._size), _data(new char[_size]) { memcpy(_data, buf._data, _size); } ~Buffer() { delete[] _data; } void operator=(const Buffer& buf) { delete[] _data; _size = buf._size; _data = new char[_size]; memcpy(_data, buf._data, _size); } const char* Data() const { return _data; } private: size_t _size; char* _data; }; // the basic instruction class Instruction { public: Instruction(opcode code) : _code(code) {} Instruction(opcode code, const char* data, size_t dataSize) : _code(code), _data(data, dataSize) {} opcode Code() const { return _code; } const char* Data() const { return _data.Data(); } private: opcode _code; Buffer _data; // additional data }; The Instruction class is now more concise, and still maintains its interface and functionality. This now leads us to the second part of tidying up. The file is somewhat cluttered. We should move the new Buffer class to a separate file, since it is more of a supporting utility. We can also move some of the larger inlined functions to a source file.. Lastly, as I mentioned at the end of the previous article, the code presented in main() is becoming a nightmarish hack. It's definitely time to introduce a utility for loading scripts dynamically. Ultimately, we will want to be able to load a script from file. However, before we deal with file handling, we will need to develop a parser to properly read in lines of text as script instructions. Simple console I/O will suit us best for this at the moment, since we do not want to introduce too many new things untested at once, otherwise we run the risk of having errors that are harder to trace. To set up our testing, we can use the getline() functionality of cin to read an entire line of input into a buffer at once: const int BUFFER_SIZE = 256; int main() { char buffer[BUFFER_SIZE]; // begin reading text lines from user cout << "Input script instructions:" << endl; cin.getline(buffer, BUFFER_SIZE); // an input processing loop will follow once we can scan the input return 0; } |