Sonntag, 5. Juni 2011

Datenwerk Schemas, Continued

I spent a day writing a schema parser, and a C++ code generator on top of it, and guess what: it's just a stupid idea. Not because providing static and typesafe access to structures is a stupid idea, but because C++ makes it tough to extend a compound of classes.

Consider following example:

// a generated set of wrapper classes

namespace Basic {

//describes a target port in a private routing system
struct Port : dwk::Object
    dwk::String name;
    dwk::Int index;

// object wrapper, describes a connection between two ports
struct PortConnection : dwk::Object
    Port source;
    Port sink;

} // namespace Basic

Above definition conveniently abstracts away Item interaction and provides type-safe access to keys, but we might want to add a few methods to PortConnection and Port later on. C++ suggests to do this via inherit & extend:

namespace App {

// extending Port with a function that dumps its content to stdout
struct Port : Basic::Port {
    void debug_print();

// same with PortConnection
struct PortConnection : Basic::PortConnection {
    void debug_print();

} // namespace App

So far so good. But if App::PortConnection::debug_print() were to call App::Port::debug_print(), we'd have to do some inconvenient casting to get around the fact that source and sink have been defined as Basic::Port:

// our implementation of PortConnection::debug_print()
namespace App {

void PortConnection::debug_print() {
    // rather ugly way to get at our functionality, and not type-safe
    (static_cast<App::Port &>(source)).debug_print();
    (static_cast<App::Port &>(sink)).debug_print();

} // namespace App

Now we could of course re-declare our members as App::Port & and initialize these as upcast references, but it's still error-prone, cumbersome, ugly.

It's therefore impossible to provide a generator for code that does not have to be touched - code generation could only be used to provide initial boilerplate code, would just help to get into things, but after the first few extensions, you'd be on your own.

Ironically, both C and Python don't carry these problems, as C does not bind methods to structs, and thus additional functions can be written to effortlessly match the already existing built-in ones, and Python does not require to dictate the type of members.

Of course, C does not allow extending structs with new data members, but neither does the approach above. In the end, providing a code generator that allows code to feel more native also re-introduces the issues that Datenwerk was supposed to overcome.

1 Kommentar: