Contents
Input Output Setup Instructions Warnings Dealing with Stragglers Protocol Other
The Middleware Writer takes two types of input and writes C++ code which can be used as part of a distributed application. The two kinds of input are C++ include files and "Middle" files.
A Middle file is required as input; include files are optional. Middle code is something we made up and is used to specify what gets written and how. Here's an example of Middle code:
MessageManager
(vector<vector<double> >, map<string, double>)
(list<Account*>, string [10])
//(vector<Account*>, string [10])
}
The name you give, in this case, MessageManager, is used as the name of the generated class. After the name, you specify one or more lines as needed. The Middleware Writer will write one or two methods based on each line. The lines have the following general form:
(T1 [ @as @nohi ], T2 [ @as @nohi ], ... Tn [ @as @nohi ]) [ @out @in @msg_id.* ]
Each line begins with a left paren. T1 through Tn are types and should follow C++ syntax. The braces indicate that what is contained in them is optional. Here are some more examples:
MessageManager
(double, string)
(int32_t, int32_t)
}
This will produce a class called MessageManager. Methods called Send and Receive are written. The method prototypes would be:
void Send(SendBuffer*, const double, const string&);
template <typename R>
void Receive(ReceiveBuffer<R>*, double&, string&);
void Send(SendBuffer*, const int32_t, const int32_t);
template <typename R>
void Receive(ReceiveBuffer<R>*, int32_t&, int32_t&);
If instead you wrote:MessageManager (double, string) @out (int32_t, int32_t) @out }only the Send methods will be written. By default both Send and Receive methods are written. If you only want one of them, use @in or @out to tailor the output.
Integer message ids can be associated with a given message by adding @msg_id... to the Middle code. For example,
MessageManager
(double, string) @msg_id_EE_a
(int32_t, int32_t) @msg_id_EE_b
}
The sendsample/ receivesample programs use message ids.
Sometimes one only needs to send a subset of a container. The adaptive send option, @as, is helpful in such circumstances. This option is only applicable with container classes and makes the most sense with a container that is sorted:
Message
(set<string> @as)
}
In this example, the generated Send function would take a set<string>::const_iterator and an integer count of how many items to send. This is handy because it helps to avoid copying a subset of the set into another container and then passing that object to the generated code. @as may only be used at the top most level. In other words, this won't work:
Message
(vector<set<int> @as >)
}
The @nohi option disables the use of insert functions that take a hint. The sorted container classes: (multi)set, (multi)map and rb_tree have insert functions that take a hint. By default, the C++ Middleware Writer will use these functions. If you want to use the non-hinted insert function, add @nohi. If you are marshalling data from one set to another, chances are the default behaviour is what you want, but if, for example, data is being marshalled from a non-sorted list or vector into a set, using @nohi will improve the performance a little.
// or /* ... */ comments are supported in Middle code. A lone } is used to indicate the end of the construct.
The second kind of input is C++ header/include files. There are some restrictions in this area. We don't support:
We accept namespace definitions, but we don't organize types with respect to namespaces. If you have two types with the same name in different namespaces, we will flag that as an error. Template support is coming along, but isn't finished. We don't support template template arguments at this time.
Here is an example of an acceptable header file:
#ifndef MENU
#define MENU
#include <string>
#include <vector>
#include <iostream>
class Counter;
class SendBuffer;
namespace llama {
// Several prototypes have to be added to your code.
// The virtual keyword is optional and depends on
// the context.
// Also, you're responsible for implementing a
// public Send function. In a class hierarchy,
// probably only the base class will need a Send
// function. There's a sample implementation
// here.
class Base
{
std::vector<std::string> baseFileNames_;
virtual inline void SendTypeNum(SendBuffer*) const;
protected:
virtual inline void SendMemberData(SendBuffer*) const;
public:
template <typename B>
explicit Base(B* buf);
template <typename B>
static Base* BuildPolyInstance(B* buf);
void Send(SendBuffer* buf, bool sendType) const
{
if (sendType) {
SendTypeNum(buf);
}
SendMemberData(buf);
}
virtual inline void CalculateMarshallingSize(Counter&) const;
};
int16_t const insert = 301;
int16_t const remove = 302;
class Derived : public Base
{
int16_t op_[12];
uint32_t offset_;
virtual inline void SendTypeNum(SendBuffer*) const;
protected:
virtual inline void SendMemberData(SendBuffer*) const;
public:
template <typename B>
explicit Derived(B* buf);
virtual inline void CalculateMarshallingSize(Counter&) const;
};
class FileInfo
{
#ifdef SERVER_SIDE
short isnew_;
long modTime_;
#endif
std::string introducedFileName_;
Derived parts_;
inline void SendTypeNum(SendBuffer* buf) const;
protected:
inline void SendMemberData(SendBuffer* buf) const;
public:
template <typename B>
explicit FileInfo(B* buf);
~FileInfo() {}
void Send(SendBuffer* buf, bool sendType) const
{
// Since this class is not derived from, there's
// no need to send type numbers.
SendMemberData(buf);
}
inline void CalculateMarshallingSize(Counter&) const;
};
}
#endif
Given the above definitions we can write Middle code that uses the types:
Messages
(deque<FileInfo*>, string)
(vector<Base*>, short)
}
There is one option that is invoked from include files. The #ifdef SERVER_SIDE line above is interpreted by the generator in a special way. This option requires some explaining.
A C++ class definition with Send/Receive functions is returned as text on the screen. If user defined types are involved, marshalling functions related to those types are also written.
The C++ Middleware Writer may write up to five marshalling related functions for a user defined type. Four of the functions are ordinary member functions and are named SendTypeNum, SendMemberData, CalculateMarshallingSize, and BuildPolyInstance. The fifth function is a stream constructor. BuildPolyInstance is needed only in the base class of a class hierarchy. Depending on your application, you may need to make some of the prototypes virtual. Here's a list of what the prototypes should be:
template <typename B>
class_name(B* buf); // stream constructor.
template <typename B>
static inline class_name* BuildPolyInstance(B* buf);
[virtual] inline void SendTypeNum(SendBuffer* buf) const;
[virtual] inline void SendMemberData(SendBuffer* buf) const;
[virtual] inline void CalculateMarshallingSize(Counter&) const;
It's your responsibility to add these to your types. The function definitions will be produced by the C++ Middleware Writer.
The rbtree example shows how to add these prototypes to classes.
Be careful with pointers. The Send functions don't check that pointers are non null.
We support Boost::multi_index_container, but with some limitations:
These limitations permit the transmission of a multi_index_container to a "single index container" such as vector or list if desired.
Often a new version of a server is required to support multiple versions of ambassadors(clients). The following is an idea of how to go about that.
release
1.1 1.2 1.3 1.4
---------------------------------------------------------
class name Account Account Account_13 Account_13
The class name incorporates the release in which it was last changed. Let's say that the 1.3 version of the server is required to support 1.1 clients. To do this the server is built with both the Account and Account_13 definitions.
When only additions to Account are needed between releases, the software might be structured like this
AccountBase
|
Account
|
Account_13
If members need to be relocated out of Account in 1.3, the hierarchy might be
AccountBase
/ \
Account Account_13
In either case, the 1.3 server may use polymorphism and accomplish what is needed pretty simply. When working with a 1.1 ambassador, it produces 1.1 data by executing what originally was 1.1 code.
There are a few simple rules that are followed:
We use a library called CGIC to support the forms on the site. The author of CGIC requires the following paragraph be included here. It has no bearing on our ability to offer this service freely. C++ source code obtained via this site, whether written by a person or a computer, is free. In the spirit of days gone by, the C++ code is not copyrighted.
CGIC, copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 by Thomas Boutell and Boutell.Com, Inc.. Permission is granted to use CGIC in any application, commercial or noncommercial, at no cost. HOWEVER, this copyright paragraph must appear on a "credits" page accessible in the public online and offline documentation of the program. Modified versions of the CGIC library should not be distributed without the attachment of a clear statement regarding the author of the modifications, and this notice may in no case be removed. Modifications may also be submitted to the author for inclusion in the main CGIC distribution.
The above does not impinge on our freedom to offer code generation services free of copyrights. C++ source code obtained through this website is not copyrighted.