Getting Started with Thrift and D

klickverbot edited this page Mar 27, 2012 · 8 revisions

Setting up your environment

First, you need a working D2/Phobos environment. Fortunately, as of DMD release 2.058, all you have to do is going to dlang.org, where you should be able to find a suitable package for your system.

Speaking of operating systems, the current code has been tested on OS X 10.6/10.7 x86/x86_64, (Arch) Linux x86/x86_64, and Windows x86 using DMD. Other D compilers (GDC, LDC) might work if they are using a recent front-end version, but have not been tested.

  • Install DMD 2.058+.

  • Recommended: Install the Deimos D headers for OpenSSL and libevent (header-only, just put them on the module search path).

  • Fetch the Thrift sources, i.e. d-gsoc branch from this very repository.

  • Initialize the build system by running ./bootstrap.sh in the Thrift directory.

  • Run ./configure. The D library is built by default if a working D2 compiler is found in the PATH; you can manually specify the (DMD-compatible) compiler to use by setting the DMD environment variable to the path of your compiler executable. The »Building D library« line in the report at the end should say »yes«.

  • Build the Thrift compiler by running make in the compiler/cpp directory.

  • Build the D Thrift library by running make in the lib/d directory.

You should now have a working build of the Thrift compiler and the Thrift D libraries. To verify that everything is working correctly, you should run the test suite by invoking make check in the lib/d directory. If you want to use make install, the D modules for Thrift are installed to $INCLUDEDIR/d2 by default, i.e. /usr/local/include/d2 if you didn't set another installation prefix. You can override this path by setting the D_IMPORT_PREFIX variable appropriately during the configure step.

Library overview

Like the Thrift implementations for the other languages, the D one fundamentally consists of two parts – a code generator for the Thrift compiler (written in C++), which generates D code from the interface definition (.thrift) files, and a complimentary D library which actually implements the different protocols, servers, etc.

Due to the powerful metaprogramming capabilities of D, this scheme was modified slightly, however, compared to, say, C++ or Java, in that the Thrift compiler merely generates a 1:1 translation of the IDL file, and most of the actual code generations happens at compile-time using template metaprogramming and compile-time function evaluation.

For example, consider the following Thrift definitions:

struct Foo {
  1: string aString,
  2: i32 anInt
}

service Service {
  Foo getFoo(),
  void setFoo(1: Foo foo)
}

The D Thrift generator creates the following code for it:

import thrift.codegen;

struct Foo {
  string aString;
  int anInt;
  
  mixin TStructHelpers!([
    TFieldMeta(`aString`, 1),
    TFieldMeta(`anInt`, 2)
  ]);
}

interface Service {
  Foo getFoo();
  void setFoo(Foo foo);

  enum methodMeta = [
    TMethodMeta(`setFoo`, [TParamMeta(`foo`, 1)])
  ];
}

As you can see, the struct and service definitions have been translated to a »native« D struct and interface, with the field ids, which have no direct equivalent in D, being expressed as »metadata« literals using the T*Meta types from thrift.codegen.

For the struct, the metadata is passed directly to the TStructHelpers template, which defines additional fields and methods so that the struct can be (de)serialized to/from a Thrift protocol and used with the Thrift »is set« mechanism – see the thrift.codegen source or DDoc documentation for details. For applications outside the typical Thrift workflow (write an IDL file -> generate stubs -> implement them), the reading/writing methods can also be used as free functions (thrift.codegen.{readStruct, writeStruct}), which also allow passing metadata via a template parameter. Special care should be taken to always construct struct instances using the parameterless static opCall is generated, so that the default values specified in the IDL file/metadata can be set (D does not allow parameterless constructors for structs):

Foo foo; // Wrong, ignores Thrift default values!
auto foo = Foo(); // Correct.

For the service resp. the interface, however, the metadata is stored in a special manifest constant called methodMeta. This is done because the interface does not define any »magic« by itself, but instead is used as parameter to other code generation templates, the most commonly used being TClient and TServiceProcessor. To illustrate this, let's implement a simple server server and client for the above Service:

// The actual implementation of the Service interface that will
// be invoked by the processor/server.
class ServiceHandler : Service {
  override Foo getFoo() {
    return foo_;
  }

  override void setFoo(Foo foo) {
    foo_ = foo;
  }

private:
  Foo foo_;
}

void main() {
  // Initialize server transport/protocol.
  ushort port = 9090;
  auto serverTransport = new TServerSocket(port);
  auto transportFactory = new TBufferedTransportFactory;
  auto protocolFactory = new TBinaryProtocolFactory!();

  // Setup a handler instance, and a service processor which reads methods
  // calls off the wire, forwards them to the handler, and writes the return
  // values back to the client.
  auto handler = new ServiceHandler;
  auto processor = new TServiceProcessor!Service(handler);

  auto server = new TSimpleServer(processor, serverTransport,
    transportFactory, protocolFactory);
  writefln("Starting the server on port %s...", port);
  server.serve();
}
void main() {
  // Initialize connection to the server.
  auto socket = new TSocket("localhost", 9090);
  auto transport = new TBufferedTransport(socket);
  auto protocol = createTBinaryProtocol(transport);
  auto client = createTClient!Service(protocol);
  transport.open();

  // Call setFoo() at the server.
  auto foo = Foo();
  foo.aString = "asdf";
  foo.anInt = 1234;
  client.setFoo(foo);

  // Make sure getFoo() returns the value set before.
  enforce(client.getFoo() == foo);
}

As you can see, besides the use of templates instead of »traditionally« generated code, the D library is conceptually very similar to the other language libraries, e.g. the C++ and Java ones. For further information, you might want to consult the general Thrift documentation and the D API documentation. More code examples can be found in the tutorial/d/ and lib/d/test/ directories.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.