The Goal: Put together a stub to build a Client/Server application where the Client/Server both use a library called Messages to implement a protocol. This post will not go into threading or asio. I only want to illustrate how to put together a project with multiple executables that relies on a single library.
Project Organization: I'm putting the client/server/messages into their own directories:
app
client
main.cpp
server
main.cpp
messages
messages.h
messages.cpp
I'll create a container object in messages that contains all message objects in the system. That way I can grab the container from client or server and print the contents. This proves that the library gets linked:
File: messages.h
#include <boost/noncopyable.hpp>
#include <string>
#include <vector>
class Message : public boost::noncopyable
{
protected:
explicit Message(std::string const& id);
public:
virtual ~Message();
std::string const& getId() const;
private:
std::string id;
};
class Messages : public boost::noncopyable
{
public:
typedef std::vector<Message*> MessagesContainer;
typedef MessagesContainer::iterator Iterator;
Messages();
~Messages();
Iterator begin();
Iterator end();
private:
MessagesContainer messages;
};
File: messages.cpp
#include "messages.h"
Message::Message(std::string const& idIn) : id(idIn) { }
Message::~Message() { }
std::string const& Message::getId() const
{
return this->id;
}
class Message1 : public Message
{
public:
Message1() : Message("message1") { }
};
Messages::Messages()
: messages()
{
this->messages.push_back(new Message1());
}
Messages::~Messages() { /* Add messages deletion */ }
Messages::Iterator Messages::begin() { return this->messages.begin(); }
Messages::Iterator Messages::end() { return this->messages.end(); }
File: main.cpp
/**
* The main is just a simple test program that
* prints out the contents of the
* messages. We don't really do anything here.
*/
#include "messages/messages.h"
#include <algorithm>
#include <iostream>
void print(Message* const& message)
{
std::cout << message->getId() << std::endl;
}
int main(int , char** )
{
Messages m;
std::for_each( m.begin(), m.end(), print );
return 0;
}
Now comes the point of this post: where do we put the Jamfiles and Jamroot files? The first experiment I did was to simply build the messages library using Boost.Build:
File: messages/Jamroot
lib messages : messages.cpp : <include>.. ;
If you don't put the semicolon at the end, you'll get an error stating:
Jamroot:1: syntax error at EOF
Building the library is a great first step- but we need to build this as part of a larger project. Let's place a Jamroot at the root of the project that will in turn build the messages library:
File: Jamroot
build-project messages ;
NOTE: Make sure to put a space between messages and the semicolon! Boost.Build is unnecessarily picky about this for some reason! This call will of course build the project messages (which has its own Jamroot file!). Let's flesh the project out a bit more with the file to build the client and server stubs:
File: client/Jamroot
exe client : main.cpp ../messages//messages : <include>.. ;
File: server/Jamroot
exe server : main.cpp ../messages//messages : <include>.. ;
File: Jamroot (EDIT)
build-project messages ;
build-project client ;
build-project server ;
NOTE: Make sure there are no spaces between <include> and the ..- If there are spaces there will be vague errors! The last file modified in the root Jamroot since we want to build client and server. Notice that in the client/server Jamroot's, we have additional dependencies that we want to build (../messages//messages).
After building, this is what the directory structure will look like:
app/
client/
main.cpp
Jamroot
bin/
gcc-4.7/
debug/
client (bin)
server
main.cpp
Jamroot
bin/
gcc-4.7/
debug/
server (bin)
messages
messages.h
messages.cpp
Jamroot
bin/
gcc-4.7/
debug/
libmessages.so (bin)
In conclusion I have to say that the parser for Boost.Build needs a lot to be desired! Positioning of whitespace shouldn't matter but it does with the Jamroot files! The documentation also perplexes me: when do I name a file a Jamfile vs. Jamroot? Also, what's the deal with using xml-like tags inside of Makefile looking syntax? Consistency is key when attracting new users. I can honestly say with confidence that I won't recommend using this tool in the future. Instead I'll fall back to premake4, qmake, or cmake as they are much easier and consistent to use.
(I would normally begin with what I found appealing; I couldn't find anything. Sorry Boost.Build!)
No comments:
Post a Comment