Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
86 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Cleaning Up
 Loading From Text
 Scanning
 Matching
 Basic File Handling
 Loading From File

 Source code
 Printable version
 Discuss this article

The Series
 An Introduction
 Data Manipulation
 Dynamic Loading
 The Stack and
 Program Flow


A Small Detour

In 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 Up

The 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;
}




Next : Loading From Text