Added sharedLog and sharedMessageDispatch libraries

This commit is contained in:
Anonymous
2014-01-14 05:51:48 -07:00
parent 765616a540
commit 23bf1cd329
56 changed files with 3985 additions and 0 deletions
+2
View File
@@ -4,8 +4,10 @@ add_subdirectory(sharedDebug)
add_subdirectory(sharedFile)
add_subdirectory(sharedFoundation)
add_subdirectory(sharedFoundationTypes)
add_subdirectory(sharedLog)
add_subdirectory(sharedMath)
add_subdirectory(sharedMemoryManager)
add_subdirectory(sharedMessageDispatch)
add_subdirectory(sharedNetworkMessages)
add_subdirectory(sharedRandom)
add_subdirectory(sharedSynchronization)
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 2.8)
project(sharedLog)
if(WIN32)
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public)
add_subdirectory(src)
@@ -0,0 +1 @@
#include "../../src/shared/ConfigSharedLog.h"
@@ -0,0 +1 @@
#include "../../src/shared/FileLogObserver.h"
@@ -0,0 +1 @@
#include "../../src/shared/FirstSharedLog.h"
@@ -0,0 +1 @@
#include "../../src/shared/Log.h"
@@ -0,0 +1 @@
#include "../../src/shared/LogManager.h"
@@ -0,0 +1 @@
#include "../../src/shared/LogObserver.h"
@@ -0,0 +1 @@
#include "../../src/shared/NetLogConnection.h"
@@ -0,0 +1 @@
#include "../../src/shared/NetLogObserver.h"
@@ -0,0 +1 @@
#include "../../src/shared/SetupSharedLog.h"
@@ -0,0 +1 @@
#include "../../src/shared/StderrLogger.h"
@@ -0,0 +1 @@
#include "../../src/shared/TailFileLogObserver.h"
@@ -0,0 +1,56 @@
set(SHARED_SOURCES
shared/ConfigSharedLog.cpp
shared/ConfigSharedLog.h
shared/TailFileLogObserver.cpp
shared/TailFileLogObserver.h
shared/FileLogObserver.cpp
shared/FileLogObserver.h
shared/FirstSharedLog.cpp
shared/FirstSharedLog.h
shared/Log.h
shared/LogManager.cpp
shared/LogManager.h
shared/LogObserver.cpp
shared/LogObserver.h
shared/NetLogConnection.cpp
shared/NetLogConnection.h
shared/NetLogObserver.cpp
shared/NetLogObserver.h
shared/SetupSharedLog.cpp
shared/SetupSharedLog.h
shared/StderrLogger.h
)
if(WIN32)
set(PLATFORM_SOURCES
win32/StderrLogger.cpp
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32)
else()
set(PLATFORM_SOURCES
linux/StderrLogger.cpp
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/linux)
endif()
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/shared
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMessageDispatch/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetwork/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetworkMessages/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedSynchronization/include/public
${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include
${SWG_EXTERNALS_SOURCE_DIR}/ours/library/fileInterface/include/public
)
add_library(sharedLog STATIC
${SHARED_SOURCES}
${PLATFORM_SOURCES}
)
@@ -0,0 +1,74 @@
// ======================================================================
//
// StderrLogger.cpp
//
// Copyright 2003 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedLog/StderrLogger.h"
#include "sharedLog/Log.h"
#include <string>
#include <fcntl.h>
// ======================================================================
namespace StderrLoggerNamespace
{
bool s_installed;
int s_oldStderr;
int s_stderrPipe[2];
}
using namespace StderrLoggerNamespace;
// ======================================================================
void StderrLogger::install()
{
DEBUG_FATAL(s_installed, ("StderrLogger already installed"));
s_installed = true;
s_oldStderr = dup(2);
pipe(s_stderrPipe);
dup2(s_stderrPipe[1], 2);
fcntl(s_stderrPipe[0], F_SETFL, O_NONBLOCK);
}
// ----------------------------------------------------------------------
void StderrLogger::remove()
{
DEBUG_FATAL(!s_installed, ("StderrLogger not installed"));
dup2(s_oldStderr, 2);
close(s_stderrPipe[0]);
close(s_stderrPipe[1]);
s_installed = false;
}
// ----------------------------------------------------------------------
void StderrLogger::update()
{
if (!s_installed)
return;
char buf[8192];
int nRead = read(s_stderrPipe[0], buf, sizeof(buf)-1);
if (nRead > 0)
{
int startPos = 0;
while (startPos < nRead)
{
int endPos = startPos;
while (buf[endPos] != '\n' && endPos < nRead)
++endPos;
buf[endPos] = 0;
if (endPos > startPos)
LOG("stderr", ("%s", buf+startPos));
startPos = endPos+1;
}
}
}
// ======================================================================
@@ -0,0 +1,114 @@
// ======================================================================
//
// ConfigSharedLog.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedLog/ConfigSharedLog.h"
#include "sharedFoundation/ConfigFile.h"
#include <vector>
// ======================================================================
namespace ConfigSharedLogNamespace
{
typedef std::vector<char const *> StringPtrArray;
int ms_logNetQueueSize;
int ms_logNetReconnectTimeMs;
bool ms_logReportLogs;
bool ms_logReportWarnings;
bool ms_logReportFatals;
bool ms_logStderr;
StringPtrArray ms_logTargets; // ConfigFile owns the pointer
}
using namespace ConfigSharedLogNamespace;
#define KEY_BOOL(a,b) (ms_ ## a = ConfigFile::getKeyBool("SharedLog", #a, b))
#define KEY_INT(a,b) (ms_ ## a = ConfigFile::getKeyInt("SharedLog", #a, b))
// ======================================================================
void ConfigSharedLog::install()
{
KEY_INT (logNetQueueSize, 2000);
KEY_INT (logNetReconnectTimeMs, 10000);
KEY_BOOL(logReportLogs, false);
KEY_BOOL(logReportWarnings, true);
KEY_BOOL(logReportFatals, true);
KEY_BOOL(logStderr, false);
int index = 0;
char const * result = 0;
do
{
result = ConfigFile::getKeyString("SharedLog", "logTarget", index++, 0);
if (result)
ms_logTargets.push_back(result);
}
while (result);
}
// ----------------------------------------------------------------------
int ConfigSharedLog::getLogNetQueueSize()
{
return ms_logNetQueueSize;
}
// ----------------------------------------------------------------------
int ConfigSharedLog::getLogNetReconnectTimeMs()
{
return ms_logNetReconnectTimeMs;
}
// ----------------------------------------------------------------------
bool ConfigSharedLog::getLogReportLogs()
{
return ms_logReportLogs;
}
// ----------------------------------------------------------------------
bool ConfigSharedLog::getLogReportWarnings()
{
return ms_logReportWarnings;
}
// ----------------------------------------------------------------------
bool ConfigSharedLog::getLogReportFatals()
{
return ms_logReportFatals;
}
// ----------------------------------------------------------------------
bool ConfigSharedLog::getLogStderr()
{
return ms_logStderr;
}
// ----------------------------------------------------------------------
int ConfigSharedLog::getNumberOfLogTargets()
{
return static_cast<int>(ms_logTargets.size());
}
// ----------------------------------------------------------------------
char const * ConfigSharedLog::getLogTarget(int const index)
{
VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, getNumberOfLogTargets());
return ms_logTargets[static_cast<size_t>(index)];
}
// ======================================================================
@@ -0,0 +1,32 @@
// ======================================================================
//
// ConfigSharedLog.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_ConfigSharedLog_H
#define INCLUDED_ConfigSharedLog_H
// ======================================================================
class ConfigSharedLog
{
public:
static void install();
static int getLogNetQueueSize();
static int getLogNetReconnectTimeMs();
static bool getLogReportLogs();
static bool getLogReportWarnings();
static bool getLogReportFatals();
static bool getLogStderr();
static int getNumberOfLogTargets();
static char const * getLogTarget(int index);
};
// ======================================================================
#endif // INCLUDED_ConfigSharedLog_H
@@ -0,0 +1,122 @@
// ======================================================================
//
// FileLogObserver.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedFoundation/NetworkIdArchive.h"
#include "sharedLog/FileLogObserver.h"
#include "sharedLog/LogManager.h"
#include "fileInterface/StdioFile.h"
#include "sharedNetworkMessages/LogMessage.h"
#include "UnicodeUtils.h"
#include <string>
#include <cstdio>
// ======================================================================
const int LOG_FILESIZE_NEXT = 2000000000; // point at which we roll over to another log file
// ======================================================================
void FileLogObserver::install()
{
LogManager::registerObserverType("file", create);
}
// ----------------------------------------------------------------------
LogObserver *FileLogObserver::create(std::string const &spec)
{
return new FileLogObserver(spec);
}
// ----------------------------------------------------------------------
FileLogObserver::FileLogObserver(std::string const &filename) :
LogObserver(),
m_filename(filename),
m_file(0),
m_fileIndex(0)
{
prepareFile();
}
// ----------------------------------------------------------------------
void FileLogObserver::prepareFile()
{
while (!m_file || m_file->tell() >= LOG_FILESIZE_NEXT)
{
if (m_file)
{
++m_fileIndex;
delete m_file;
m_file = 0;
}
if (m_fileIndex == 0)
m_file = new StdioFile(m_filename.c_str(), "a");
else
{
char buf[512];
IGNORE_RETURN( snprintf(buf, 512, "%s-%d", m_filename.c_str(), m_fileIndex+1) );
m_file = new StdioFile(buf, "a");
}
NOT_NULL(m_file);
FATAL(!m_file->isOpen(), ("Could not open %s", m_filename.c_str()));
}
}
// ----------------------------------------------------------------------
FileLogObserver::~FileLogObserver()
{
delete m_file;
}
// ----------------------------------------------------------------------
void FileLogObserver::log(LogMessage const &msg)
{
prepareFile();
NOT_NULL(m_file);
uint64 timestamp = msg.getTimestamp();
std::string const &procId = msg.getProcId();
std::string const &channel = msg.getChannel();
std::string const &text = msg.getText();
std::string uniAttach(Unicode::wideToNarrow(msg.getUnicodeAttach()));
char tsbuf[16]; // yyyymmddhhmmss (14)
IGNORE_RETURN( sprintf(tsbuf, UINT64_FORMAT_SPECIFIER, timestamp) );
IGNORE_RETURN( m_file->write(14, tsbuf) );
IGNORE_RETURN( m_file->write(1, ":") );
IGNORE_RETURN( m_file->write(static_cast<int>(procId.length()), procId.c_str()) );
IGNORE_RETURN( m_file->write(1, ":") );
IGNORE_RETURN( m_file->write(static_cast<int>(channel.length()), channel.c_str()) );
IGNORE_RETURN( m_file->write(1, ":") );
IGNORE_RETURN( m_file->write(static_cast<int>(text.length()), text.c_str()) );
if (uniAttach.length())
{
IGNORE_RETURN( m_file->write(1, ":") );
IGNORE_RETURN( m_file->write(static_cast<int>(uniAttach.length()), uniAttach.c_str()) );
}
IGNORE_RETURN( m_file->write(1, "\n") );
if (LogManager::getFlushOnWrite())
m_file->flush();
}
// ----------------------------------------------------------------------
void FileLogObserver::flush()
{
if (m_file)
m_file->flush();
}
// ======================================================================
@@ -0,0 +1,52 @@
// ======================================================================
//
// FileLogObserver.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_FileLogObserver_H
#define INCLUDED_FileLogObserver_H
// ======================================================================
#include "sharedLog/LogObserver.h"
#include <string>
// ======================================================================
class AbstractFile;
// ======================================================================
// A FileLogObserver saves log messages to a file
class FileLogObserver: public LogObserver
{
public:
static void install();
static LogObserver *create(std::string const &spec);
FileLogObserver(std::string const &filename);
virtual ~FileLogObserver();
virtual void log(LogMessage const &msg);
virtual void flush();
private:
FileLogObserver(FileLogObserver const &);
FileLogObserver &operator=(FileLogObserver const &);
void prepareFile();
private:
std::string m_filename;
AbstractFile *m_file;
int m_fileIndex;
};
// ======================================================================
#endif
@@ -0,0 +1,10 @@
// ======================================================================
//
// FirstSharedLog.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
@@ -0,0 +1,19 @@
// ======================================================================
//
// FirstSharedLog.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_FirstSharedLog_H
#define INCLUDED_FirstSharedLog_H
// ======================================================================
#include "sharedFoundation/FirstSharedFoundation.h"
// ======================================================================
#endif
@@ -0,0 +1,39 @@
// ======================================================================
//
// Log.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_Log_H
#define INCLUDED_Log_H
// ======================================================================
#include "LogManager.h"
// ======================================================================
// Un-conditional
#define LOG(stream, varArgs) (LogManager::setArgs(stream), LogManager::log varArgs)
#define LOGU(stream, varArgs, unicodeAttach) (LogManager::setArgs(stream, unicodeAttach), LogManager::log varArgs)
// Conditional
#define LOGC(forceLog, stream, varArgs) ((forceLog) ? (LogManager::setArgs(stream), LogManager::log varArgs) : NOP)
#define LOGUC(forceLog, stream, varArgs, unicodeAttach) ((forceLog) ? (LogManager::setArgs(stream, unicodeAttach), LogManager::log varArgs) : NOP)
#ifdef _DEBUG
#define DEBUG_LOG(stream, varArgs) LOG(stream, varArgs)
#define DEBUG_LOGU(stream, varArgs, unicodeAttach) LOGU(stream, varArgs, unicodeAttach)
#else
#define DEBUG_LOG(stream, varArgs) NOP
#define DEBUG_LOGU(stream, varArgs, unicodeAttach) NOP
#endif
// ======================================================================
#endif
@@ -0,0 +1,367 @@
// ======================================================================
//
// LogManager.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedFoundation/NetworkIdArchive.h"
#include "sharedFoundation/ExitChain.h"
#include "sharedFoundation/ConfigFile.h"
#include "sharedLog/LogManager.h"
#include "sharedLog/LogObserver.h"
#include "sharedLog/ConfigSharedLog.h"
#include "sharedLog/StderrLogger.h"
#include "sharedNetworkMessages/LogMessage.h"
#include "sharedSynchronization/RecursiveMutex.h"
#include <list>
#include <map>
#include <string>
#include <cstdio>
#include <time.h>
// ======================================================================
typedef std::list<LogObserver *> ObserverList;
typedef std::map<std::string, LogObserverCreateFunc> ObserverCreateMap;
// ======================================================================
namespace LogManagerNamespace
{
void remove();
const int MaxLogMessageLen = 16384;
struct LogManagerData
{
LogManagerData() :
mutex(),
observers(),
observerCreateMap(),
channel(),
unicodeAttach(),
processIdentifier(),
flushOnWrite(true),
logging(0)
{
}
RecursiveMutex mutex;
ObserverList observers;
ObserverCreateMap observerCreateMap;
std::string channel;
Unicode::String unicodeAttach;
std::string processIdentifier;
bool flushOnWrite;
int logging; // used for catching recursive log attempts from the same thread
private:
LogManagerData(LogManagerData const &);
LogManagerData &operator=(LogManagerData const &);
};
LogManagerData *s_data;
}
using namespace LogManagerNamespace;
// ======================================================================
void LogManager::install(std::string const &procId, bool flushOnWrite)
{
DEBUG_FATAL(s_data, ("LogManager already installed"));
s_data = new LogManagerData;
s_data->processIdentifier = procId;
s_data->flushOnWrite = flushOnWrite;
ExitChain::add(LogManagerNamespace::remove, "LogManagerNamespace::remove");
if (ConfigSharedLog::getLogStderr())
StderrLogger::install();
}
// ----------------------------------------------------------------------
void LogManager::update()
{
if (!s_data)
return;
if (ConfigSharedLog::getLogStderr())
StderrLogger::update();
for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i)
(*i)->update();
}
// ----------------------------------------------------------------------
void LogManagerNamespace::remove()
{
DEBUG_FATAL(!s_data, ("LogManager not installed"));
// we own any observers we have, so delete them
while (!s_data->observers.empty())
{
delete (*s_data->observers.begin());
IGNORE_RETURN( s_data->observers.erase(s_data->observers.begin()) );
}
if (ConfigSharedLog::getLogStderr())
StderrLogger::remove();
delete s_data;
s_data = 0;
}
// ----------------------------------------------------------------------
void LogManager::setProcessIdentifier(std::string const &procId)
{
s_data->processIdentifier = procId;
}
// ----------------------------------------------------------------------
void LogManager::setArgs(std::string const &channel, Unicode::String const &unicodeAttach)
{
if (!s_data)
return;
s_data->mutex.enter(); // leave() called from log which should always follow setArgs
if (s_data->logging == 0)
{
++s_data->logging;
s_data->channel = channel;
s_data->unicodeAttach = unicodeAttach;
}
}
// ----------------------------------------------------------------------
void LogManager::setArgs(std::string const &channel)
{
if (!s_data)
return;
s_data->mutex.enter(); // leave() called from log which should always follow setArgs
++s_data->logging;
if (s_data->logging == 1)
{
s_data->channel = channel;
s_data->unicodeAttach.clear();
}
}
// ----------------------------------------------------------------------
void LogManager::log(char const *format, ...)
{
if (!s_data)
return;
// if noone is observing log messages, no need to process them
if (s_data->observers.size() && s_data->logging == 1)
{
++s_data->logging;
static char text[MaxLogMessageLen];
{
va_list ap;
va_start(ap, format); //lint !e746 !e1055
IGNORE_RETURN( _vsnprintf(text, sizeof(text), format, ap) );
text[sizeof(text)-1] = '\0';
size_t len = strlen(text);
// if string was truncated, stick a + on the end
if (len == sizeof(text)-1)
text[len-1] = '+';
va_end(ap);
}
uint64 timestamp;
{
// format current date/time gmt as yyyymmddhhmmss, in a uint64
time_t now;
tm t;
IGNORE_RETURN( time(&now) );
IGNORE_RETURN( gmtime_r(&now, &t) );
timestamp = t.tm_year+1900; //lint !e732 !e737 !e776
timestamp *= 100;
timestamp += t.tm_mon+1; //lint !e737 !e776
timestamp *= 100;
timestamp += static_cast<unsigned int>(t.tm_mday);
timestamp *= 100;
timestamp += static_cast<unsigned int>(t.tm_hour);
timestamp *= 100;
timestamp += static_cast<unsigned int>(t.tm_min);
timestamp *= 100;
timestamp += static_cast<unsigned int>(t.tm_sec);
}
observeLogMessage(LogMessage(timestamp, s_data->processIdentifier, s_data->channel, text, s_data->unicodeAttach));
--s_data->logging;
}
--s_data->logging;
s_data->mutex.leave(); // enter() called from setArgs which should always precede log
}
// ----------------------------------------------------------------------
void LogManager::observeLogMessage(LogMessage const &msg)
{
for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i)
if (!(*i)->isFiltered(msg))
(*i)->log(msg);
}
// ----------------------------------------------------------------------
void LogManager::addObserver(LogObserver *observer)
{
s_data->observers.push_back(observer);
}
// ----------------------------------------------------------------------
void LogManager::removeObserver(LogObserver const *observer)
{
// we own any observers given to us, so delete it when it is removed
for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i)
{
if ((*i) == observer)
{
delete (*i);
IGNORE_RETURN( s_data->observers.erase(i) );
break;
}
}
}
// ----------------------------------------------------------------------
void LogManager::registerObserverType(std::string const &name, LogObserverCreateFunc func)
{
s_data->observerCreateMap[name] = func;
char buffer[256];
sprintf(buffer, "%s:", name.c_str());
// run through SharedLog keys looking for logTarget=name:
{
int const numberLogTargets = ConfigSharedLog::getNumberOfLogTargets();
for (int i = 0; i < numberLogTargets; ++i)
{
char const * const result = ConfigSharedLog::getLogTarget(i);
if (result && strstr(result, buffer) == result)
{
setupObserver(result);
}
}
}
// run through SharedLog keys looking for logTarget#=name:
{
char key[16];
strcpy(key, "logTarget");
for (int i = 0; i < 20; ++i)
{
char const * result = 0;
sprintf(key+9, "%d", i);
int count = 0;
do
{
result = ConfigFile::getKeyString("SharedLog", key, count, 0);
if (result && strstr(result, buffer) == result)
setupObserver(result);
++count;
} while (result);
}
}
}
// ----------------------------------------------------------------------
bool LogManager::setupObserver(std::string const &desc)
{
REPORT_LOG(true, ("Log observer setup: %s\n", desc.c_str()));
// description should be "type:spec{filter1:filter2:...}"
size_t pos = desc.find(":");
if (pos != std::string::npos)
{
std::string type(desc.substr(0, pos));
std::string spec;
std::string filt;
size_t pos2 = desc.find("{");
if (pos2 == std::string::npos)
spec = desc.substr(pos+1);
else
{
spec = desc.substr(pos+1, (pos2-(pos+1)));
filt = desc.substr(pos2+1);
if (filt.length() && filt[filt.length()-1] == '}')
{
IGNORE_RETURN( filt.erase(filt.length()-1) );
}
}
ObserverCreateMap::iterator i = s_data->observerCreateMap.find(type);
if (i != s_data->observerCreateMap.end())
{
LogObserver *observer = (*(*i).second)(spec);
if (observer)
{
if (filt.length())
observer->setFilter(filt);
addObserver(observer);
return true;
}
}
}
return false;
}
// ----------------------------------------------------------------------
bool LogManager::getFlushOnWrite()
{
return s_data->flushOnWrite;
}
// ----------------------------------------------------------------------
void LogManager::flush()
{
for (ObserverList::iterator i = s_data->observers.begin(); i != s_data->observers.end(); ++i)
(*i)->flush();
}
// ----------------------------------------------------------------------
void LogManager::logLongText(std::string const & channel, std::string const & longText)
{
int const targetLineLength=80;
int const maxLineLength=100;
char buffer[maxLineLength+1];
int pos=0;
for (std::string::const_iterator i=longText.begin(); i!=longText.end(); ++i)
{
buffer[pos++]=*i;
if ((pos>=targetLineLength && *i==' ') || (pos==maxLineLength))
{
buffer[pos]='\0';
setArgs(channel);
log("%s", buffer);
pos=0;
}
}
if (pos!=0)
{
buffer[pos]='\0';
setArgs(channel);
log("%s", buffer);
pos=0;
}
}
// ======================================================================
@@ -0,0 +1,58 @@
// ======================================================================
//
// LogManager.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_LogManager_H
#define INCLUDED_LogManager_H
// ======================================================================
class LogMessage;
class LogObserver;
// ======================================================================
// The LogManager is a static class which catches log messages,
// processes them from varArgs into straight ascii text, and passes the
// processed string on to any observers, along with a timestamp,
// process identifier, and any unicode text attached.
typedef LogObserver *(*LogObserverCreateFunc)(std::string const &);
class LogManager
{
public:
static void install(std::string const &procId, bool flushOnWrite = true);
static void update();
static void setProcessIdentifier(std::string const &procId);
// note: setArgs() and log() always come as a pair, and must do so to avoid deadlocks
static void setArgs(std::string const &channel, Unicode::String const &unicodeAttach);
static void setArgs(std::string const &channel);
static void log(char const *format, ...);
static void logLongText(std::string const & channel, std::string const & longText);
static void observeLogMessage(LogMessage const &msg);
static void addObserver(LogObserver *observer);
static void removeObserver(LogObserver const *observer);
static void registerObserverType(std::string const &name, LogObserverCreateFunc func);
static bool setupObserver(std::string const &desc);
static bool getFlushOnWrite();
static void flush();
private:
LogManager();
LogManager(LogManager const &);
LogManager &operator=(LogManager const &);
};
// ======================================================================
#endif
@@ -0,0 +1,111 @@
// ======================================================================
//
// LogObserver.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedFoundation/NetworkIdArchive.h"
#include "sharedLog/LogObserver.h"
#include "sharedNetworkMessages/LogMessage.h"
#include <string>
#include <vector>
// ======================================================================
LogObserver::LogObserver() :
m_filters(new std::vector<std::string>())
{
}
// ----------------------------------------------------------------------
LogObserver::~LogObserver()
{
delete m_filters;
}
// ----------------------------------------------------------------------
void LogObserver::setFilter(std::string const &filter)
{
// filter is a : separated list of filter rules. split them and add
// valid ones to the filter list
m_filters->clear();
size_t curPos = 0;
while (curPos < filter.length())
{
size_t endPos = filter.find(":", curPos);
if (endPos == std::string::npos)
endPos = filter.length()+1;
m_filters->push_back(filter.substr(curPos, endPos-curPos));
curPos = endPos+1;
}
}
// ----------------------------------------------------------------------
bool LogObserver::isFiltered(LogMessage const &msg) const
{
bool filtered = false;
// run through the list of filters comparing them
std::vector<std::string>::const_iterator i;
for (i = m_filters->begin(); i != m_filters->end(); ++i)
{
std::string const &filter = (*i);
std::string const *source = 0;
// source specifier (c or d - channel or data)
if (filter[0] == 'c')
source = &msg.getChannel();
else if (filter[0] == 'd')
source = &msg.getText();
else if (filter[0] == 'p')
source = &msg.getProcId();
else
continue; // invalid source specifier, skip filter
// filter action (+ or -)
bool action = false;
if (filter[1] == '+')
action = false;
else if (filter[1] == '-')
action = true;
else
continue; // invalid action specifier, skip filter
// may be a comparison negation specifier
size_t filterPos = 2;
bool negateCompare = false;
if (filter[2] == '!')
{
++filterPos;
negateCompare = true;
}
bool match;
// either '*' or a substring to search for, may change this to regex
if (filter[filterPos] == '*')
match = true;
else
match = (source->find(filter.c_str()+filterPos) != std::string::npos);
if (negateCompare)
match = !match;
if (match)
filtered = action;
}
return filtered;
}
// ----------------------------------------------------------------------
void LogObserver::flush()
{
}
//------------------------------------------------------------------------------------------
void LogObserver::update()
{
}
// ======================================================================
@@ -0,0 +1,47 @@
// ======================================================================
//
// LogObserver.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_LogObserver_H
#define INCLUDED_LogObserver_H
// ======================================================================
class LogMessage;
// ======================================================================
// A LogObserver is derived from in order to provide a mechanism for
// catching log messages and doing something with them. A derived
// observer object must be added to the logManager before it will
// have any effect.
class LogObserver
{
public:
LogObserver();
virtual ~LogObserver();
virtual void log(LogMessage const &msg) = 0;
virtual void flush();
void setFilter(std::string const &filter);
bool isFiltered(LogMessage const &msg) const;
virtual void update();
private:
LogObserver(LogObserver const &);
LogObserver &operator=(LogObserver const &);
stdvector<std::string>::fwd* m_filters;
};
// ======================================================================
#endif
@@ -0,0 +1,69 @@
// ======================================================================
//
// NetLogConnection.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedFoundation/NetworkIdArchive.h"
#include "sharedLog/NetLogObserver.h"
#include "sharedNetwork/NetworkSetupData.h"
#include "NetLogConnection.h"
// ======================================================================
NetLogConnection::NetLogConnection(NetLogObserver *owner) :
Connection(owner->getRemoteAddress(), owner->getRemotePort(), NetworkSetupData()),
m_owner(owner)
{
}
// ----------------------------------------------------------------------
NetLogConnection::~NetLogConnection()
{
if (m_owner)
m_owner->onConnectionDestroyed(this);
m_owner = 0;
}
// ----------------------------------------------------------------------
void NetLogConnection::onConnectionClosed()
{
if (m_owner)
m_owner->onConnectionClosed();
}
// ----------------------------------------------------------------------
void NetLogConnection::onConnectionOpened()
{
if (m_owner)
m_owner->onConnectionOpened();
}
// ----------------------------------------------------------------------
void NetLogConnection::clearOwner()
{
m_owner = 0;
}
// ----------------------------------------------------------------------
void NetLogConnection::onReceive(Archive::ByteStream const &)
{
}
// ----------------------------------------------------------------------
bool NetLogConnection::isNetLogConnection() const
{
return true;
}
// ======================================================================
@@ -0,0 +1,48 @@
// ======================================================================
//
// NetLogConnection.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_NetLogConnection_H
#define INCLUDED_NetLogConnection_H
// ======================================================================
#include "sharedNetwork/Connection.h"
// ======================================================================
class NetLogObserver;
// ======================================================================
class NetLogConnection: public Connection
{
public:
explicit NetLogConnection(NetLogObserver *owner);
virtual ~NetLogConnection();
virtual void onConnectionClosed();
virtual void onConnectionOpened();
virtual void onReceive(Archive::ByteStream const &);
void clearOwner();
protected:
virtual bool isNetLogConnection() const;
private:
NetLogConnection();
NetLogConnection(NetLogConnection const &);
NetLogConnection &operator=(NetLogConnection const &);
NetLogObserver *m_owner;
};
// ======================================================================
#endif
@@ -0,0 +1,174 @@
// ======================================================================
//
// NetLogObserver.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedLog/NetLogObserver.h"
#include "sharedFoundation/Clock.h"
#include "sharedFoundation/NetworkIdArchive.h"
#include "sharedLog/LogManager.h"
#include "sharedLog/ConfigSharedLog.h"
#include "sharedNetworkMessages/GameNetworkMessage.h"
#include "sharedNetworkMessages/LogMessage.h"
#include "Archive/ByteStream.h"
#include "NetLogConnection.h"
#include <string>
#include <vector>
// ======================================================================
void NetLogObserver::install()
{
LogManager::registerObserverType("net", create);
}
// ----------------------------------------------------------------------
LogObserver *NetLogObserver::create(std::string const &spec)
{
// parse spec into address:port
size_t pos = spec.find(":");
if (pos != std::string::npos)
{
std::string address = spec.substr(0, pos);
unsigned short port = static_cast<unsigned short>(atoi(spec.c_str()+pos+1));
// create a NetLogObserver to forward to the specified address/port
return new NetLogObserver(address, port);
}
return 0;
}
// ----------------------------------------------------------------------
NetLogObserver::NetLogObserver(const std::string &remoteAddress, unsigned short remotePort) :
LogObserver(),
m_mutex(),
m_remoteAddress(remoteAddress),
m_remotePort(remotePort),
m_connection(0),
m_connectionOpen(false),
m_connectTime(0),
m_logMessageQueue1(new std::vector<LogMessage>),
m_logMessageQueue2(new std::vector<LogMessage>)
{
}
// ----------------------------------------------------------------------
NetLogObserver::~NetLogObserver()
{
delete m_logMessageQueue1;
delete m_logMessageQueue2;
m_logMessageQueue1 = 0;
m_logMessageQueue2 = 0;
// the observer owns the connection
if (m_connection)
{
m_connection->clearOwner();
m_connection->setDisconnectReason("NetLogObserver destroyed");
m_connection->disconnect();
m_connection = 0;
}
}
// ----------------------------------------------------------------------
void NetLogObserver::onConnectionOpened()
{
m_connectionOpen = true;
}
// ----------------------------------------------------------------------
void NetLogObserver::onConnectionClosed()
{
m_connectionOpen = false;
if (m_connection)
{
m_connection->clearOwner();
m_connection = 0;
}
}
// ----------------------------------------------------------------------
void NetLogObserver::onConnectionDestroyed(NetLogConnection *connection)
{
if (m_connection == connection)
m_connection = 0;
}
// ----------------------------------------------------------------------
void NetLogObserver::sendLogMessage(LogMessage const &msg) const
{
if (m_connection)
{
Archive::ByteStream a;
a.clear();
msg.pack(a);
m_connection->send(a, true);
}
}
// ----------------------------------------------------------------------
void NetLogObserver::log(LogMessage const &msg)
{
static unsigned int logNetQueueSize = static_cast<unsigned int>(ConfigSharedLog::getLogNetQueueSize());
if (logNetQueueSize)
{
m_mutex.enter();
if (m_logMessageQueue1->size() < logNetQueueSize)
m_logMessageQueue1->push_back(msg);
m_mutex.leave();
}
}
//------------------------------------------------------------------------------------------
void NetLogObserver::update()
{
static unsigned long reconnectTime = static_cast<unsigned long>(ConfigSharedLog::getLogNetReconnectTimeMs());
if (m_connection && m_connectionOpen)
{
m_mutex.enter();
std::vector<LogMessage> *q = m_logMessageQueue1;
m_logMessageQueue1 = m_logMessageQueue2;
m_logMessageQueue2 = q;
m_mutex.leave();
// send any queued up log messages
for (std::vector<LogMessage>::const_iterator i = m_logMessageQueue2->begin(); i != m_logMessageQueue2->end(); ++i)
sendLogMessage(*i);
m_logMessageQueue2->clear();
}
else if (Clock::timeMs()-m_connectTime > reconnectTime)
connect();
}
// ----------------------------------------------------------------------
void NetLogObserver::connect()
{
if (m_connection)
{
m_connection->clearOwner();
m_connection->setDisconnectReason("About to connect again");
m_connection->disconnect();
m_connection = 0;
}
m_connectTime = Clock::timeMs();
m_connection = new NetLogConnection(this);
}
// ======================================================================
@@ -0,0 +1,80 @@
// ======================================================================
//
// NetLogObserver.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_NetLogObserver_H
#define INCLUDED_NetLogObserver_H
// ======================================================================
#include "sharedLog/LogObserver.h"
#include "sharedSynchronization/Mutex.h"
#include <string>
// ======================================================================
class NetLogConnection;
// ======================================================================
// A NetLogObserver sends log messages to a another process
class NetLogObserver: public LogObserver
{
public:
static void install();
static LogObserver *create(std::string const &spec);
NetLogObserver(std::string const &remoteAddress, unsigned short remotePort);
virtual ~NetLogObserver();
virtual void log(LogMessage const &msg);
virtual void onConnectionOpened();
virtual void onConnectionClosed();
virtual void onConnectionDestroyed(NetLogConnection *connection);
virtual void update();
std::string const &getRemoteAddress() const;
unsigned short getRemotePort() const;
private:
NetLogObserver();
NetLogObserver(NetLogObserver const &);
NetLogObserver &operator=(NetLogObserver const &);
void connect();
void sendLogMessage(LogMessage const &msg) const;
Mutex m_mutex;
std::string m_remoteAddress;
unsigned short m_remotePort;
NetLogConnection * m_connection;
bool m_connectionOpen;
unsigned long m_connectTime;
stdvector<LogMessage>::fwd* m_logMessageQueue1;
stdvector<LogMessage>::fwd* m_logMessageQueue2;
};
// ======================================================================
inline std::string const &NetLogObserver::getRemoteAddress() const
{
return m_remoteAddress;
}
// ----------------------------------------------------------------------
inline unsigned short NetLogObserver::getRemotePort() const
{
return m_remotePort;
}
// ======================================================================
#endif
@@ -0,0 +1,92 @@
// ======================================================================
//
// SetupSharedLog.cpp
//
// Copyright 2002-2003 Sony Online Entertainment
// All Rights Reserved.
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedLog/SetupSharedLog.h"
#include "sharedDebug/InstallTimer.h"
#include "sharedFoundation/ExitChain.h"
#include "sharedLog/ConfigSharedLog.h"
#include "sharedLog/FileLogObserver.h"
#include "sharedLog/NetLogObserver.h"
#include "sharedLog/LogManager.h"
#include "sharedLog/Log.h"
#include "sharedLog/TailFileLogObserver.h"
// ======================================================================
namespace SetupSharedLogNamespace
{
void logReportLog(char const *message);
void logReportWarning(char const *message);
void logReportFatal(char const *message);
bool ms_installed;
std::string const cms_reportLogChannel("reportLog");
std::string const cms_reportWarningChannel("warning");
std::string const cms_reportFatalChannel("fatal");
}
using namespace SetupSharedLogNamespace;
// ======================================================================
void SetupSharedLogNamespace::logReportLog(char const *message)
{
LOG(cms_reportLogChannel, ("%s", message));
}
// ----------------------------------------------------------------------
void SetupSharedLogNamespace::logReportWarning(char const *message)
{
LOG(cms_reportWarningChannel, ("%s", message));
}
// ----------------------------------------------------------------------
void SetupSharedLogNamespace::logReportFatal(char const *message)
{
LOG(cms_reportFatalChannel, ("%s", message));
}
// ======================================================================
void SetupSharedLog::install(std::string const &procId, bool flushOnWrite)
{
InstallTimer const installTimer("SetupSharedLog::install");
DEBUG_FATAL (ms_installed, ("SetupSharedLog::install already installed"));
ms_installed = true;
ConfigSharedLog::install();
LogManager::install(procId, flushOnWrite);
FileLogObserver::install();
NetLogObserver::install();
TailFileLogObserver::install();
if (ConfigSharedLog::getLogReportLogs())
Report::bindLogCallback(logReportLog);
if (ConfigSharedLog::getLogReportWarnings())
Report::bindWarningCallback(logReportWarning);
if (ConfigSharedLog::getLogReportFatals())
Report::bindFatalCallback(logReportFatal);
}
// ----------------------------------------------------------------------
void SetupSharedLog::remove()
{
DEBUG_FATAL (!ms_installed, ("SetupSharedLog::remove not installed"));
ms_installed = false;
}
// ======================================================================
@@ -0,0 +1,31 @@
// ======================================================================
//
// SetupSharedLog.h
//
// Copyright 2002-2003 Sony Online Entertainment
// All Rights Reserved.
//
// ======================================================================
#ifndef INCLUDED_SetupSharedLog_H
#define INCLUDED_SetupSharedLog_H
// ======================================================================
class SetupSharedLog
{
public:
static void install(std::string const &procId, bool flushOnWrite = true);
static void remove();
private:
SetupSharedLog(SetupSharedLog const &);
SetupSharedLog &operator=(SetupSharedLog const &);
};
// ======================================================================
#endif
@@ -0,0 +1,26 @@
// ======================================================================
//
// StderrLogger.h
//
// Copyright 2003 Sony Online Entertainment
//
// ======================================================================
#ifndef _StderrLogger_H_
#define _StderrLogger_H_
// ======================================================================
class StderrLogger
{
public:
static void install();
static void remove();
static void update();
};
// ======================================================================
#endif // _StderrLogger_H_
@@ -0,0 +1,188 @@
// ======================================================================
//
// TailFileLogObserver.cpp
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedFoundation/NetworkIdArchive.h"
#include "sharedLog/TailFileLogObserver.h"
#include "sharedLog/LogManager.h"
#include "fileInterface/StdioFile.h"
#include "sharedNetworkMessages/LogMessage.h"
#include "UnicodeUtils.h"
#include <string>
#include <cstdio>
// ======================================================================
const int LOG_FILESIZE_NEXT = 2000000000; // point at which we roll over to another log file
const int DEFAULT_NUM_LINES = 100;
namespace TailFileLogObserverNamespace
{
std::vector<TailFileLogObserver *> s_tailFileLogObservers;
};
using namespace TailFileLogObserverNamespace;
// ======================================================================
void TailFileLogObserver::install()
{
LogManager::registerObserverType("tailfile", create);
}
// ----------------------------------------------------------------------
LogObserver *TailFileLogObserver::create(std::string const &spec)
{
return new TailFileLogObserver(spec);
}
// ----------------------------------------------------------------------
TailFileLogObserver::TailFileLogObserver(std::string const &filename) :
LogObserver(),
m_filename(filename),
m_file(0),
m_fileIndex(0),
m_numLines(DEFAULT_NUM_LINES),
m_textBuffer(0),
m_nextTextBufferEntry(0)
{
int filenameAsNumber = -1;
filenameAsNumber = atoi(m_filename.c_str());
m_numLines = filenameAsNumber;
std::string::size_type const index = filename.find_last_of(',');
if(index != std::string::npos)
m_filename = filename.substr(index + 1, filename.length());
else
m_filename.clear();
m_textBuffer = new std::vector<std::string>(m_numLines);
s_tailFileLogObservers.push_back(this);
}
// ----------------------------------------------------------------------
void TailFileLogObserver::setNumberOfLinesToKeep(int numLines)
{
m_numLines = numLines;
m_textBuffer->clear();
m_nextTextBufferEntry = 0;
m_textBuffer->reserve(m_numLines);
}
// ----------------------------------------------------------------------
void TailFileLogObserver::prepareFile()
{
while (!m_file || m_file->tell() >= LOG_FILESIZE_NEXT)
{
if (m_file)
{
++m_fileIndex;
delete m_file;
m_file = 0;
}
if (m_fileIndex == 0)
m_file = new StdioFile(m_filename.c_str(), "a");
else
{
char buf[512];
IGNORE_RETURN( snprintf(buf, 512, "%s-%d", m_filename.c_str(), m_fileIndex+1) );
m_file = new StdioFile(buf, "a");
}
NOT_NULL(m_file);
FATAL(!m_file->isOpen(), ("Could not open %s", m_filename.c_str()));
}
}
// ----------------------------------------------------------------------
TailFileLogObserver::~TailFileLogObserver()
{
delete m_file;
delete m_textBuffer;
}
// ----------------------------------------------------------------------
void TailFileLogObserver::log(LogMessage const &msg)
{
std::string & newLogMessage = (*m_textBuffer)[m_nextTextBufferEntry++];
uint64 timestamp = msg.getTimestamp();
std::string const &procId = msg.getProcId();
std::string const &channel = msg.getChannel();
std::string const &text = msg.getText();
std::string uniAttach(Unicode::wideToNarrow(msg.getUnicodeAttach()));
char tsbuf[16]; // yyyymmddhhmmss (14)
newLogMessage.clear();
IGNORE_RETURN( sprintf(tsbuf, UINT64_FORMAT_SPECIFIER, timestamp) );
newLogMessage.append(tsbuf);
newLogMessage.append(":");
newLogMessage.append(procId);
newLogMessage.append(":");
newLogMessage.append(channel);
newLogMessage.append(":");
newLogMessage.append(text);
if(uniAttach.length())
{
newLogMessage.append(":");
newLogMessage.append(uniAttach);
}
if(m_nextTextBufferEntry >= m_numLines)
m_nextTextBufferEntry = 0;
}
// ----------------------------------------------------------------------
void TailFileLogObserver::flush(const char * filename)
{
if(m_filename.empty())
m_filename = filename;
prepareFile();
NOT_NULL(m_file);
int i;
for(i = m_nextTextBufferEntry; i < m_numLines; ++i)
{
if(!(*m_textBuffer)[i].empty())
{
IGNORE_RETURN(m_file->write((*m_textBuffer)[i].length(), (*m_textBuffer)[i].c_str()) );
}
}
for(i = 0; i < m_nextTextBufferEntry; ++i)
{
if(!(*m_textBuffer)[i].empty())
{
IGNORE_RETURN(m_file->write((*m_textBuffer)[i].length(), (*m_textBuffer)[i].c_str()) );
}
}
if (m_file)
m_file->flush();
m_file->close();
}
// ----------------------------------------------------------------------
void TailFileLogObserver::flushAllTailFileLogObservers(const char * filename)
{
for(std::vector<TailFileLogObserver *>::iterator i = s_tailFileLogObservers.begin(); i != s_tailFileLogObservers.end(); ++i)
{
TailFileLogObserver *tflo = *i;
if(tflo)
{
tflo->flush(filename);
}
}
}
// ======================================================================
@@ -0,0 +1,61 @@
// ======================================================================
//
// TailFileLogObserver.h
//
// Copyright 2002 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_TailFileLogObserver_H
#define INCLUDED_TailFileLogObserver_H
// ======================================================================
#include "sharedLog/LogObserver.h"
#include <string>
// ======================================================================
class AbstractFile;
// ======================================================================
// A TailFileLogObserver saves log messages to a file, but only the last N,
// and only when requested (not constantly).
class TailFileLogObserver: public LogObserver
{
public:
static void install();
static LogObserver *create(std::string const &spec);
TailFileLogObserver(std::string const &filename);
virtual ~TailFileLogObserver();
virtual void log(LogMessage const &msg);
virtual void flush(const char * filename);
virtual void setNumberOfLinesToKeep(int numLines);
static void flushAllTailFileLogObservers(const char *filename);
private:
TailFileLogObserver(TailFileLogObserver const &);
TailFileLogObserver &operator=(TailFileLogObserver const &);
void prepareFile();
private:
std::string m_filename;
AbstractFile *m_file;
int m_fileIndex;
int m_numLines;
stdvector<std::string>::fwd *m_textBuffer;
int m_nextTextBufferEntry;
};
// ======================================================================
#endif
@@ -0,0 +1,35 @@
// ======================================================================
//
// StderrLogger.cpp
//
// Copyright 2003 Sony Online Entertainment
//
// ======================================================================
#include "sharedLog/FirstSharedLog.h"
#include "sharedLog/StderrLogger.h"
// ======================================================================
// Unimplemented for win32
// ======================================================================
void StderrLogger::install()
{
}
// ----------------------------------------------------------------------
void StderrLogger::remove()
{
}
// ----------------------------------------------------------------------
void StderrLogger::update()
{
}
// ======================================================================
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 2.8)
project(sharedMessageDispatch)
if(WIN32)
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public)
add_subdirectory(src)
@@ -0,0 +1 @@
#include "../../src/shared/Emitter.h"
@@ -0,0 +1 @@
#include "../../src/shared/FirstSharedMessageDispatch.h"
@@ -0,0 +1 @@
#include "../../src/shared/Message.h"
@@ -0,0 +1 @@
#include "../../src/shared/MessageManager.h"
@@ -0,0 +1 @@
#include "../../src/shared/Receiver.h"
@@ -0,0 +1 @@
#include "../../src/shared/Transceiver.h"
@@ -0,0 +1,37 @@
set(SHARED_SOURCES
shared/Emitter.cpp
shared/Emitter.h
shared/FirstSharedMessageDispatch.h
shared/Message.cpp
shared/Message.h
shared/MessageManager.cpp
shared/MessageManager.h
shared/Receiver.cpp
shared/Receiver.h
shared/Transceiver.cpp
shared/Transceiver.h
)
if(WIN32)
set(PLATFORM_SOURCES
win32/FirstSharedMessageDispatch.cpp
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32)
else()
set(PLATFORM_SOURCES "")
endif()
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/shared
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public
${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public
)
add_library(sharedMessageDispatch STATIC
${SHARED_SOURCES}
${PLATFORM_SOURCES}
)
@@ -0,0 +1,268 @@
//---------------------------------------------------------------------
#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"
#include "sharedDebug/Profiler.h"
#include "sharedMessageDispatch/Emitter.h"
#include "sharedMessageDispatch/MessageManager.h"
#include "sharedMessageDispatch/Receiver.h"
#include "sharedMessageDispatch/Message.h"
#include <cassert>
#include <set>
#include <map>
namespace MessageDispatch {
struct Emitter::ReceiverList
{
typedef std::set<Receiver *> ReceiverSet;
typedef std::map<unsigned long int, ReceiverSet> Container;
mutable Container c;
};
//---------------------------------------------------------------------
/**
@brief Construct an emitter
An Emitter is an abstract base. This is never directly invoked.
@author Justin Randall
*/
Emitter::Emitter() :
receiverList(new ReceiverList)
{
assert (receiverList != NULL);
}
//---------------------------------------------------------------------
/**
@brief pure virtual destructor
The Emitter destructor cleans up message dispatch relationships
*/
Emitter::~Emitter()
{
for(ReceiverList::Container::iterator i = receiverList->c.begin(); i != receiverList->c.end(); ++i)
{
ReceiverList::ReceiverSet & targets = (*i).second;
for(ReceiverList::ReceiverSet::iterator j = targets.begin(); j != targets.end(); ++j)
{
Receiver * r = (*j);
r->emitterDestroyed(*this);
}
}
delete receiverList;
receiverList = 0;
}
//---------------------------------------------------------------------
/**
@brief Attach a Receiver object directly to this Emitter
When a Receiver needs to specifically target a particular emitter,
it should invoke addReceiver and specify the message type it is
subscribing to.
@param target The Receiver object that will receive the
specified message when the Emitter emits.
@param messageTypeName The message type that the Emitter should
deliver to the target Receiver.
@see Receiver
@see MessageManager::addReceiver
@author Justin Randall
*/
void Emitter::addReceiver(Receiver & target, const char * const messageTypeName) const
{
const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName);
addReceiver(target, messageType);
}
//-----------------------------------------------------------------------
void Emitter::addReceiver(Receiver & target, const MessageBase & source) const
{
const unsigned long int messageType = source.getType();
addReceiver(target, messageType);
}
//-----------------------------------------------------------------------
void Emitter::addReceiver(Receiver & target, const unsigned long int messageType) const
{
ReceiverList::Container::iterator i = receiverList->c.find(messageType);
if(i == receiverList->c.end())
{
ReceiverList::ReceiverSet newSet;
newSet.insert(&target); //lint !e534 // ignoring iterator returned from insert
receiverList->c.insert (std::make_pair (messageType, newSet));
}
else
{
ReceiverList::ReceiverSet & s = (*i).second;
s.insert(&target); //lint !e534 // ignoring iterator returned from insert
}
}
//---------------------------------------------------------------------
/**
@brief emit a message
All targeted Receivers will receive the Message that is emitted by
this Emitter object. The Message is forwarded to the MessageManager
to be dispatched to objects that are interested in receiving ALL
messages of a specific type, regardless of who emits it. Those
Receiver objects will only receive the Message via the MessageManager
if they did NOT receive the message from this Emitter object as a
targetted message.
@param message A const reference to a MessageBase object that will
be delivered to registered Receivers.
@see addReceiver
@see MessageManager::addReceiver
@author Justin Randall
*/
void Emitter::emitMessage(const MessageBase & message) const
{
NOT_NULL(receiverList);
ReceiverList::Container::iterator i = receiverList->c.find(message.getType());
if(i != receiverList->c.end())
{
//-- make copy
const ReceiverList::ReceiverSet targets = (*i).second;
for(ReceiverList::ReceiverSet::const_iterator j = targets.begin(); j != targets.end(); ++j)
{
Receiver * r = (*j);
NOT_NULL(r);
r->receiveMessage(*this, message);
}
}
MessageManager::getInstance().emitMessage(*this, message);
}
//---------------------------------------------------------------------
/**
@brief helper to determine if an Emitter has a Receiver in it's target
list
This helper is invoked by the MessageManager to determine if a
Receiver is registered as a target for a message type. When the
Emitter emits a message, all Receiver objects registered with the
Emitter will receive the message. If a Receiver object is ALSO registered
with the MessageManager to receive ALL messages of the requested type,
the MessageManager invokes this method on the source Emitter object
to ensure that the Receiver target is not delivered the same message
twice.
@param target The receiver to search for
@param messageType The MessageBase typeID of the message that the
receiver is interested in.
@return true if this Emitter object has the Receiver mapped to the
messageType in it's target list.
@see MessageManager
@author Justin Randall
*/
const bool Emitter::hasReceiver(const Receiver & target, const unsigned long int messageType) const
{
bool result = false;
ReceiverList::Container::const_iterator i = receiverList->c.find(messageType);
if(i != receiverList->c.end())
{
const ReceiverList::ReceiverSet & targets = (*i).second;
const ReceiverList::ReceiverSet::const_iterator j = targets.find(const_cast<Receiver *>(&target)); //stl is broken
result = (j != targets.end());
}
return result;
}
//---------------------------------------------------------------------
/**
@brief helper to determine if an Emitter has a Receiver in it's target
list
This helper is invoked to determine if a Receiver is registered as a
target for any message type.
@param target The receiver to search for
@return true if this Emitter object has the Receiver mapped to any messageType
@see MessageManager
@author John Watson
*/
const bool Emitter::hasReceiver(const Receiver & target) const
{
for (ReceiverList::Container::const_iterator i = receiverList->c.begin (); i != receiverList->c.end (); ++i)
{
const ReceiverList::ReceiverSet & targets = (*i).second;
const ReceiverList::ReceiverSet::const_iterator j = targets.find(const_cast<Receiver *>(&target)); //stl is broken
if (j != targets.end())
return true;
}
return false;
}
//---------------------------------------------------------------------
/**
@brief remove a receiver from target lists
This is most often invoked from a Receiver object's destructor. It
removes the receiver from all Message mappings.
@param target The receiver that is being destroyed
@author Justin Randall
*/
void Emitter::receiverDestroyed(const Receiver & target) const
{
// find the receiver
for(ReceiverList::Container::iterator i = receiverList->c.begin(); i != receiverList->c.end(); ++i)
{
ReceiverList::ReceiverSet & targets = (*i).second;
const ReceiverList::ReceiverSet::iterator j = targets.find(const_cast<Receiver *>(&target)); //stl is broken
if(j != targets.end())
{
targets.erase(j);
}
}
}
//---------------------------------------------------------------------
/**
@brief Remove a Receiver object from a specific message in the target map
@param target The Receiver object that will break the
Target->message relationship.
@param messageTypeName A message type describing the message type the
Receiver will disassociate itself from
@author Justin Randall
*/
void Emitter::removeReceiver(const Receiver & target, const char * const messageTypeName) const
{
unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName);
const ReceiverList::Container::iterator i = receiverList->c.find(messageType);
if(i != receiverList->c.end())
{
ReceiverList::ReceiverSet & rset = (*i).second;
//const cast for broken stl
rset.erase(const_cast<Receiver *>(&target)); //lint !e534 // ignoring iterator returned from insert
}
}
//---------------------------------------------------------------------
}//namespace MessageDispatch
@@ -0,0 +1,60 @@
// ======================================================================
//
// Emitter.h
// copyright 2001 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_Emitter_H
#define INCLUDED_Emitter_H
// ======================================================================
namespace MessageDispatch
{
class MessageBase;
class Receiver;
/**
@brief Class that can emit messages
Emitter objects can transmit Message objects, which are dispatched
to receivers either directly, or via the MessageManager.
@see Message
@see MessageBase
@see Receiver
@see MessageManager
@author Justin Randall
*/
class Emitter
{
public:
Emitter();
virtual ~Emitter();
void addReceiver(Receiver & target, const char * const messageTypeName) const;
void addReceiver(Receiver & target, const MessageBase & source) const;
void addReceiver(Receiver & target, const unsigned long int messageType) const;
void emitMessage(const MessageBase & message) const;
const bool hasReceiver(const Receiver & target, const unsigned long int messageType) const;
const bool hasReceiver(const Receiver & target) const;
void receiverDestroyed(const Receiver & target) const;
void removeReceiver(const Receiver & target, const char * const messageTypeName) const;
private:
struct ReceiverList;
ReceiverList * receiverList;
};
}// namespace MessageDispatch
// ======================================================================
#endif
@@ -0,0 +1,23 @@
// ======================================================================
//
// FirstMessageDispatch.h
// copyright (c) 2001 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_FirstMessageDispatch_H
#define INCLUDED_FirstMessageDispatch_H
// ======================================================================
#include "sharedFoundation/FirstSharedFoundation.h"
#include "sharedMessageDispatch/Emitter.h"
#include "sharedMessageDispatch/MessageManager.h"
#include "sharedMessageDispatch/Message.h"
#include "sharedMessageDispatch/Receiver.h"
#include <set>
#include <hash_map>
// ======================================================================
#endif
@@ -0,0 +1,81 @@
// ======================================================================
//
// FirstMessageDispatch.h
// copyright (c) 2001 Sony Online Entertainment
//
// ======================================================================
#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"
#include "sharedMessageDispatch/Message.h"
#include "sharedFoundation/LabelHash.h"
#include "sharedFoundation/Crc.h"
#include "sharedFoundation/Production.h"
#include <string>
namespace MessageDispatch
{
// ======================================================================
/**
@brief build a message type to identify a message
Returns an STL hash of the type name.
*/
const unsigned long int MessageBase::makeMessageTypeFromString(const char * const idString)
{
unsigned long int result = 0;
#if PRODUCTION == 0
result = LabelHash::hashLabel("MessageDispatch", idString);
#else
result = Crc::calculate(idString);
#endif
return result;
}
//---------------------------------------------------------------------
/**
@brief construct a named message object
The MessageBase is an abstract base class. Initializes the type
member to the STL hash of the name string.
@author Justin Randall
*/
MessageBase::MessageBase(const char * const typeName) :
type(0)
{
type = makeMessageTypeFromString(typeName);
}
//---------------------------------------------------------------------
/**
@brief construct a named message object
The MessageBase is an abstract base class. Initializes the type
the supplied type. Useful when the hash is cached.
@author Justin Randall
*/
MessageBase::MessageBase(const unsigned long int newType) :
type(newType)
{
}
//---------------------------------------------------------------------
/**
@brief destroy the MessageBase object
Doesn't do anything special.
@author Justin Randall
*/
MessageBase::~MessageBase()
{
}
// ======================================================================
} // namespace MessageDispatch
@@ -0,0 +1,210 @@
// ======================================================================
//
// Message.h
// copyright 2001 Sony Online Entertainment
//
// ======================================================================
#ifndef INCLUDED_Message_H
#define INCLUDED_Message_H
// ======================================================================
namespace MessageDispatch
{
/**
@brief A base class defining a common interface for Message object
handling.
The MessageBase is a pure virtual class. All Message objects
handled by Emitter and Receiver objects derive from the MessageBase.
@see Message
@author Justin Randall
*/
class MessageBase
{
public:
explicit MessageBase(const char * const typeName);
explicit MessageBase(const unsigned long int type);
virtual ~MessageBase();
static const unsigned long int makeMessageTypeFromString(const char * const id);
const unsigned long int getType() const;
const bool isType(const char * const typeName) const;
void setType(const unsigned long int newType);
void setType(const char * const typeName);
private:
MessageBase();
unsigned long int type;
};
/**
@brief Message template
Builds a message from some user supplied class.
@author Justin Randall
*/
template<class ValueType>
class Message : public MessageBase
{
public:
explicit Message(const char * const typeName="");
explicit Message(const unsigned long int type);
Message(const char * const typeName, const ValueType & value);
Message(const unsigned long int type, const ValueType & value);
explicit Message(const Message & source);
virtual ~Message();
Message & operator = (const Message & rhs);
const ValueType & getValue() const;
void setValue(const ValueType & rhs);
private:
ValueType value;
};
//---------------------------------------------------------------------
/**
@brief get the type of Message
@return the type of the Message. The type is an STL hash of the
type name.
*/
inline const unsigned long int MessageBase::getType() const
{
return type;
}
//-----------------------------------------------------------------------
inline const bool MessageBase::isType(const char * const typeName) const
{
return (makeMessageTypeFromString(typeName) == type);
}
//-----------------------------------------------------------------------
/**
@brief set or reset the type identifier
*/
inline void MessageBase::setType(const unsigned long newType)
{
type = newType;
}
//-----------------------------------------------------------------------
/**
@brief set or reset the type identifier
*/
inline void MessageBase::setType(const char * const typeName)
{
setType(makeMessageTypeFromString(typeName));
}
//-----------------------------------------------------------------------
/**
@brief construct a named message
@param typeName A string describing the type of message. This
value is hashed (using the STL hash function)
the result is used for the MessageBase type name.
@author Justin Randall
*/
template<class ValueType>
inline Message<ValueType>::Message(const char * const typeName) :
MessageBase(typeName),
value()
{
}
template<class ValueType>
inline Message<ValueType>::Message(const unsigned long int type) :
MessageBase(type),
value()
{
}
//---------------------------------------------------------------------
/**
@brief Construct a named message and initialize it to the
supplied ValueType value.
@param typeName A string describing the type of message. This
value is hashed (using the STL hash function)
the result is used for the MessageBase type name.
@newValue Some value to initialize the message object to.
@author Justin Randall
*/
template<class ValueType>
inline Message<ValueType>::Message(const char * const typeName, const ValueType & newValue) :
MessageBase(typeName),
value(newValue)
{
}
template<class ValueType>
inline Message<ValueType>::Message(const unsigned long int type, const ValueType & newValue) :
MessageBase(type),
value(newValue)
{
}
//---------------------------------------------------------------------
/**
@brief Message copy constructor
Performs a copy of the message type and value members.
@author Justin Randall
*/
template<class ValueType>
inline Message<ValueType>::Message(const Message<ValueType> & source) :
MessageBase(source.getType()),
value(source.getValue())
{
}
//---------------------------------------------------------------------
/**
@brief destroy the Message object
Doesn't do anything special.
@author Justin Randall
*/
template<class ValueType>
inline Message<ValueType>::~Message()
{
}
//---------------------------------------------------------------------
/**
@brief Assign the souce ValueType to the value member
@param source the new value for the Message::value member
@author Justin Randall
*/
template<class ValueType>
inline void Message<ValueType>::setValue(const ValueType & source)
{
value = source;
}
//---------------------------------------------------------------------
/**
@brief return the Message::value member
@return the value of the value member
*/
template<class ValueType>
inline const ValueType & Message<ValueType>::getValue() const
{
return value;
}
}//namespace MessageDispatch
// ======================================================================
#endif // _Message_H
@@ -0,0 +1,225 @@
#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"
#include "sharedDebug/Profiler.h"
#include "sharedMessageDispatch/Emitter.h"
#include "sharedMessageDispatch/Message.h"
#include "sharedMessageDispatch/MessageManager.h"
#include "sharedMessageDispatch/Receiver.h"
#include <set>
#include <hash_map>
namespace MessageDispatch {
MessageManager MessageManager::ms_instance;
struct MessageManager::Data
{
std::hash_map<unsigned long int, std::set<Receiver *> > receivers;
std::hash_map<unsigned long int, std::set<void (*)(const Emitter &, const MessageBase &)> > staticCallbacks;
};
//---------------------------------------------------------------------
/**
@brief Do NOT construct Singleton objects directly!
*/
MessageManager::MessageManager()
: data (new Data)
{
}
//---------------------------------------------------------------------
/**
@brief Do NOT destroy Singleton objects directly!
*/
MessageManager::~MessageManager()
{
delete data;
data = 0;
}
//---------------------------------------------------------------------
/**
@brief add a Receiver object to a map of Receiver objects that will
receive all messages of a certain type.
Some Receiver objects may wish to receive a message of a certain
type regardless of the Emitter object that originates the message.
@param target The Receiver object that will listen for all
messages of the requested type
@param messageTypeName The message type to listen for
@see Emitter::addReceiver
@author Justin Randall
*/
void MessageManager::addReceiver(Receiver & target, const char * const messageTypeName)
{
const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName);
addReceiver(target, messageType);
}
//-----------------------------------------------------------------------
void MessageManager::addReceiver(Receiver & target, const MessageBase & source)
{
const unsigned long int messageType = source.getType();
addReceiver(target, messageType);
}
//-----------------------------------------------------------------------
void MessageManager::addReceiver(Receiver & target, const unsigned long int messageType)
{
std::hash_map<unsigned long int, std::set<Receiver *> >::iterator i = data->receivers.find(messageType);
if(i != data->receivers.end())
{
target.setHasTargets(true);
std::set<Receiver *> & targets = (*i).second;
targets.insert(&target);//lint !e534 // ignoring iterator returned from insert
}
else
{
target.setHasTargets(true);
std::set<Receiver *> newTargets;
newTargets.insert(&target);//lint !e534 // ignoring iterator returned from insert
data->receivers[messageType] = newTargets;
}
}
//-----------------------------------------------------------------------
void MessageManager::addStaticCallback(void (*callback)(const Emitter &, const MessageBase &), const unsigned long int messageType)
{
std::hash_map<unsigned long int, std::set<void (*)(const Emitter &, const MessageBase &)> >::iterator f = data->staticCallbacks.find(messageType);
if(f != data->staticCallbacks.end())
{
std::set<void (*)(const Emitter &, const MessageBase &)> & targets = f->second;
targets.insert(callback);
}
else
{
std::set<void (*)(const Emitter &, const MessageBase &)> newTargets;
newTargets.insert(callback);
data->staticCallbacks[messageType] = newTargets;
}
}
//---------------------------------------------------------------------
/**
@brief Invoked by Emitter objects when a message is broadcast
@param emitter The source of the MessageBase object being broadcast
@param message The message being broadcast
@see Emitter::emit
@see Receiver::onReceive
@author Justin Randall
*/
void MessageManager::emitMessage(const Emitter & emitter, const MessageBase & message) const
{
const unsigned long int messageType = message.getType();
std::hash_map<unsigned long int, std::set<Receiver *> >::const_iterator i = data->receivers.find(messageType);
if(i != data->receivers.end())
{
const std::set<Receiver *> targets = (*i).second;
std::set<Receiver *>::const_iterator j;
for(j = targets.begin(); j != targets.end(); ++j)
{
Receiver * r = (*j);
if(! emitter.hasReceiver(*r, messageType))
{
r->receiveMessage(emitter, message);
}
}
}
std::hash_map<unsigned long int, std::set<void (*)(const Emitter &, const MessageBase &)> >::iterator f = data->staticCallbacks.find(messageType);
if(f != data->staticCallbacks.end())
{
const std::set<void (*)(const Emitter &, const MessageBase &)> targets = f->second;
std::set<void (*)(const Emitter &, const MessageBase &)>::const_iterator c;
for(c = targets.begin(); c != targets.end(); ++c)
{
void (*callback)(const Emitter &, const MessageBase &) = *c;
callback(emitter, message);
}
}
}
//---------------------------------------------------------------------
/**
@brief Invoked by a Receiver during it's destructor
*/
void MessageManager::receiverDestroyed(const Receiver & target)
{
if (!target.getHasTargets())
{
return;
}
// find receiver
std::hash_map<unsigned long int, std::set<Receiver *> >::iterator i;
for(i = data->receivers.begin(); i != data->receivers.end(); ++i)
{
std::set<Receiver *> & targets = (*i).second;
// const cast to satisfy STL semantics, target remains unchanged
std::set<Receiver *>::iterator j = targets.find(const_cast<Receiver *>(&target));
if(j != targets.end())
{
targets.erase(j);
}
}
}
//---------------------------------------------------------------------
/**
@brief break a Receiver->Message relationship
When a Receiver no longer wants to receive all messages of a certain
type, it invokes MessageManager::removeReceiver to irradicate
any relationship with the message in the MessageManager target map.
@param target The Receiver object that is breaking the
connection.
@param messageTypeName Identifies the source MessageBase objects
that the Receiver object will now ignore.
@see Emitter::removeReceiver
@author Justin Randall
*/
void MessageManager::removeReceiver(const Receiver & target, const char * const messageTypeName)
{
const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName);
removeReceiver(target, messageType);
}
//-----------------------------------------------------------------------
void MessageManager::removeReceiver(const Receiver & target, const unsigned long int messageType)
{
std::hash_map<unsigned long int, std::set<Receiver *> >::iterator i = data->receivers.find(messageType);
if(i != data->receivers.end())
{
std::set<Receiver *> & targets = (*i).second;
std::set<Receiver *>::iterator j = targets.find(const_cast<Receiver *>(&target));
if(j != targets.end())
{
targets.erase(j);
}
}
}
//-----------------------------------------------------------------------
void connectToMessage(const char * const messageTypeName, void (*callback)(const Emitter &, const MessageBase &))
{
const unsigned long int messageType = MessageBase::makeMessageTypeFromString(messageTypeName);
MessageManager::getInstance().addStaticCallback(callback, messageType);
}
//---------------------------------------------------------------------
}//namespace MessageDispatch
@@ -0,0 +1,57 @@
#ifndef INCLUDED_MessageManager_H
#define INCLUDED_MessageManager_H
namespace MessageDispatch
{
//---------------------------------------------------------------------
class Emitter;
class MessageBase;
class Receiver;
void connectToMessage(const char * const messageTypeName, void (*)(const Emitter &, const MessageBase &));
//---------------------------------------------------------------------
/**
@brief A Singleton class responsible for broadcasting and tracking
Receiver, Emitter and Message objects
@author Justin Randall
*/
class MessageManager
{
public:
MessageManager();
~MessageManager();
void addReceiver(Receiver & target, const char * const messageTypeName);
void addReceiver(Receiver & target, const MessageBase & source);
void addReceiver(Receiver & target, const unsigned long int messageType);
void addStaticCallback(void (*)(const Emitter &, const MessageBase &), const unsigned long int messageTypeName);
void emitMessage(const Emitter & emitter, const MessageBase & message) const;
void receiverDestroyed(const Receiver & target);
void removeReceiver(const Receiver & target, const char * const messageTypeName);
void removeReceiver(const Receiver & target, const unsigned long int messageType);
static MessageManager & getInstance ();
private:
struct Data;
Data *data;
static MessageManager ms_instance;
};
//----------------------------------------------------------------------
inline MessageManager & MessageManager::getInstance ()
{
return ms_instance;
}
//---------------------------------------------------------------------
}//namespace MessageDispatch
#endif // _MessageManager_H
@@ -0,0 +1,215 @@
//---------------------------------------------------------------------
#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"
#include "sharedDebug/Profiler.h"
#include "sharedMessageDispatch/Emitter.h"
#include "sharedMessageDispatch/MessageManager.h"
#include "sharedMessageDispatch/Message.h"
#include "sharedMessageDispatch/Receiver.h"
#include <cassert>
#include <set>
#include <map>
namespace MessageDispatch {
struct Receiver::EmitterTargets
{
typedef std::set<const Emitter *> Container;
Container c;
};
//---------------------------------------------------------------------
/**
@brief construct a Receiver object
Receiver objects are abstract base classes. Do NOT instantiate
a Receiver directly!
@author Justin Randall
*/
Receiver::Receiver() :
emitterTargets(new EmitterTargets),
hasTargets(false)
{
assert(emitterTargets != NULL);
}
//---------------------------------------------------------------------
void Receiver::setHasTargets(bool targets)
{
hasTargets = targets;
}
//---------------------------------------------------------------------
bool Receiver::getHasTargets() const
{
return hasTargets;
}
//---------------------------------------------------------------------
/**
@brief destroy the Receiver object
Finds all Emitter objects that this Receiver is connected to and
invokes Emitter::receiveMessagerDestroyed
It then advises the MessageManager that the Receiver object is
being destroyed.
@author Justin Randall
*/
Receiver::~Receiver()
{
disconnectAll();
delete emitterTargets;
emitterTargets = 0;
}
//---------------------------------------------------------------------
/**
@brief connect to an Emitter to receive a message that it emits
When an Emitter object emits a message, it dispatches the message
to all subscribed receivers before sending it to the MessageManager
for broadcasting.
Receiver objects are inserted into the Emitter target map by way of
this function (which wraps Emitter::addReceiver)
@param target The Emitter object to connect to
@param messageTypeName The message type to listen for
@see Emitter::addReceiver
@see MessageManager::addReceiver
@author Justin Randall
*/
void Receiver::connectToEmitter(const Emitter & target, const char * const messageTypeName)
{
target.addReceiver(*this, messageTypeName);
emitterTargets->c.insert (&target);
}
//-----------------------------------------------------------------------
/**
@brief advise the MessageManager that this Reciever object is
interested in ALL messages of the requested type.
@param messageTypeName A string describing the type of message
@author Justin Randall
*/
void Receiver::connectToMessage(const char * const messageTypeName)
{
MessageManager::getInstance().addReceiver(*this, messageTypeName);
}
//-----------------------------------------------------------------------
void Receiver::connectToMessage(const MessageBase & source)
{
MessageManager::getInstance().addReceiver(*this, source);
}
//-----------------------------------------------------------------------
void Receiver::disconnectAll()
{
EmitterTargets::Container::iterator i;
for(i = emitterTargets->c.begin(); i != emitterTargets->c.end(); ++i)
{
const Emitter * e = (*i);
e->receiverDestroyed(*this);
}
emitterTargets->c.clear();
MessageManager::getInstance().receiverDestroyed(*this);
}
//-----------------------------------------------------------------------
void Receiver::disconnectFromMessage(const char * const messageTypeName)
{
disconnectFromMessage(MessageBase::makeMessageTypeFromString(messageTypeName));
}
//-----------------------------------------------------------------------
void Receiver::disconnectFromMessage(const unsigned long int messageType)
{
MessageManager::getInstance().removeReceiver(*this, messageType);
}
//-----------------------------------------------------------------------
void Receiver::disconnectFromMessage(const MessageBase & source)
{
disconnectFromMessage(source.getType());
}
//---------------------------------------------------------------------
/**
@brief helper to clean up destroyed emitter objects
If an Emitter is destroyed, but is not removed from it's target
Receiver objects emitterTargets, the Receiver objects could
attempt to invoke methods on an Emitter that no longer exists (e.g.
during destruction, when the Reciever attempts to notify the
Emitter that it is going away).
@param target The Emitter that is being destroyed
@see Emitter::receiverDestroyed
@author Justin Randall
*/
void Receiver::emitterDestroyed(Emitter & target)
{
emitterTargets->c.erase (&target);
}
//----------------------------------------------------------------------
/**
@brief stop receiving a specific message type from an emitter
@param target The Emitter that we want to stop listening for messagetype from
@param messageType The messagetype we want to stop listening to on the target
@see Emitter::connectToEmitter
@author John Watson
*/
void Receiver::disconnectFromEmitter(const Emitter & target, const char * const messageType)
{
target.removeReceiver (*this, messageType);
if (!target.hasReceiver (*this))
emitterTargets->c.erase (&target);
}
//---------------------------------------------------------------------
GlobalFunctionReceiver::GlobalFunctionReceiver(Function function)
: Receiver(),
m_function(function)
{
}
//---------------------------------------------------------------------
GlobalFunctionReceiver::~GlobalFunctionReceiver()
{
}
//---------------------------------------------------------------------
void GlobalFunctionReceiver::receiveMessage(const Emitter &source, const MessageBase &message)
{
m_function(source, message);
}
//---------------------------------------------------------------------
}//namespace MessageDispatch
@@ -0,0 +1,116 @@
#ifndef INCLUDED_Receiver_H
#define INCLUDED_Receiver_H
//---------------------------------------------------------------------
#pragma warning ( disable : 4786 ) // symbol truncated to 255 characters in debug info
namespace MessageDispatch
{
//---------------------------------------------------------------------
class MessageBase;
class Emitter;
//---------------------------------------------------------------------
/**
@brief Abstract base class describing an object that can receive
messages emitted by Emitter objects.
@author Justin Randall
*/
class Receiver
{
public:
Receiver();
virtual ~Receiver();
void connectToEmitter(const Emitter & target, const char * const messageTypeName);
void connectToEmitter(const Emitter & target, const MessageBase & source);
void connectToMessage(const char * const messageTypeName);
void connectToMessage(const MessageBase & source);
void disconnectFromEmitter(const Emitter & target, const char * const messageType);
void disconnectFromEmitter(const Emitter & target, const MessageBase & source);
void disconnectFromEmitter(const Emitter & target, const unsigned long int messageType);
void disconnectFromMessage(const char * const messageTypeName);
void disconnectFromMessage(const MessageBase & source);
void disconnectFromMessage(const unsigned long int messageType);
void disconnectAll();
void emitterDestroyed(Emitter & target);
void setHasTargets(bool targets);
bool getHasTargets() const;
/** Pure virtual */
virtual void receiveMessage(const Emitter & source, const MessageBase & message) = 0;
private:
struct EmitterTargets;
EmitterTargets * emitterTargets;
bool hasTargets;
};
// ----------------------------------------------------------------------
class GlobalFunctionReceiver : public Receiver
{
public:
typedef void (*Function)(const Emitter &, const MessageBase &);
public:
GlobalFunctionReceiver(Function function);
virtual ~GlobalFunctionReceiver();
virtual void receiveMessage(const Emitter &source, const MessageBase &message);
private:
Function m_function;
};
// ----------------------------------------------------------------------
template <typename T>
class MemberFunctionReceiver : public Receiver
{
public:
typedef void (T::*TMemberFunction)(const Emitter &, const MessageBase &);
public:
MemberFunctionReceiver(T &object, TMemberFunction memberFunction);
virtual ~MemberFunctionReceiver();
virtual void receiveMessage(const Emitter &source, const MessageBase &message);
private:
T *m_object;
TMemberFunction m_memberFunction;
};
// ----------------------------------------------------------------------
template <typename T>
MemberFunctionReceiver<T>::MemberFunctionReceiver(T &object, TMemberFunction memberFunction)
: Receiver(),
m_object(&object),
m_memberFunction(memberFunction)
{
}
template <typename T>
MemberFunctionReceiver<T>::~MemberFunctionReceiver()
{
}
template <typename T>
void MemberFunctionReceiver<T>::receiveMessage(const Emitter &source, const MessageBase &message)
{
(m_object->*m_memberFunction)(source, message);
}
//---------------------------------------------------------------------
}//namespace MessageDispatch
#endif // _Receiver_H
@@ -0,0 +1,69 @@
// Transceiver.cpp
// Copyright 2000-01, Sony Online Entertainment Inc., all rights reserved.
// Author: Justin Randall
//-----------------------------------------------------------------------
#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"
#include "sharedMessageDispatch/Transceiver.h"
#include <map>
namespace MessageDispatch {
//-----------------------------------------------------------------------
TransceiverBase::GlobalReceiverInfo::GlobalReceiverInfo() :
receivers(),
pendingAdds(),
pendingRemoves(),
locked(false)
{
}
//-----------------------------------------------------------------------
TransceiverBase::TransceiverBase() :
locked(false)
{
}
//-----------------------------------------------------------------------
TransceiverBase::~TransceiverBase()
{
}
//-----------------------------------------------------------------------
TransceiverBase::GlobalReceiverInfo & TransceiverBase::getGlobalReceiverInfo(const type_info & typeId)
{
static std::map<const char * const, GlobalReceiverInfo> receiverSets;
GlobalReceiverInfo & result = receiverSets[typeId.name()];
return result;
}
//-----------------------------------------------------------------------
Callback::Callback() :
receivers()
{
}
//-----------------------------------------------------------------------
Callback::~Callback()
{
std::vector<TransceiverBase *>::iterator i;
TransceiverBase * t;
for(i = receivers.begin(); i != receivers.end(); ++i)
{
t = (*i);
delete t;
}
}
//-----------------------------------------------------------------------
}//namespace MessageDispatch
@@ -0,0 +1,670 @@
// Copyright 2000-01, Sony Online Entertainment Inc., all rights reserved.
// Author: Justin Randall
//-----------------------------------------------------------------------
#ifndef _Transceiver_H
#define _Transceiver_H
/**
Transceiver.h
Message dispatch to non-static object member functions
<----- Messages from Transceivers
deliver to OBJECT::foo(Type)
which is a non-static member
function.
<-----------------------
+----------------------+ +-------------------+
| OBJECT | +---+ Transceiver<Type> |
+-+--------------------+ | +-------------------+
| |Callback m_callback | |
| +-+------------------+ | +-------------------+
| |OwnedTransceiver |------+---+ Transceiver<Type> |
| +------------------+ | +-------------------+
| |ptr to member func| |
| +------------------+ | +-------------------+
+ | +---+ Transceiver<Type> +
| +--------------------+ +-------------------+
+-|void foo(Type) |
| +--------------------+
| |
+----------------------+
Transceivers may be used as anonymous "broadcast" sources
delivering messages to anyone listing for a particular *type*
of message, or they may have direct relationships with other
transceivers. The relationship is defined by the receiving
object, not the transceiver. The receiver will connect() to
either another transceiver, or to a type of message. It specifies
the address of a member function that is capable of receiving
the type of message emitted from another transceiver.
When the emitting transceiver dispatches a message, it looks
through a vector of receivers for direct message dispatch, then
delivers messages to each receiver in the list. It also maintains
a list of pending removes, and skips transceivers (really
OwnedTransceiver objects) that have been deleted to prevent
dispatch to a destroyed object.
After direct dispatches are delievered, the transceiver finds
a "gobal" dispatch list that contains receivers that are connected
to the *type* of message this transceiver emits, rahter than this
transceiver specifically. The message is delivered to all recepients
in that global list. Like the local dispatch, the global dispatch also
checks for destroyed objects and skips delivery if they are no
longer valid.
Clients of dispatch code use to interfaces: Callback objects, which
are responsible for connecting object member functions to transceivers,
and Transceiver objects themselves. Client code may either implement
directed dispatch from object to object using a has-a relationship. The
emitting object will have a Transceiver, the receiving object will
have a Callback. It is the responsibility of the client to ensure
that their Transceiver or Callback objects are destroyed when their
owning objects are destroyed.
Client code may also emit messages by simply declaring a Transceiver
on the stack and invoking emitMessage() with the correct parameter type
for the message. Any objects that connect() to a message type rather
than a specific transceiver will receive the message.
Usage:
// anonymous dispatch example
class MyClass
{
public:
MyClass()
{
m_callback = new Callback;
// connect to any transceiver emitting a bool
m_callback->connect(*this, &MyClass::foo);
}
~MyClass()
{
// ensure transceivers don't deliver to this
// object anymore
delete m_callback;
}
protected:
void foo(bool);
private:
Callback * m_callback;
};
// code somewhere else (could be anywhere)
void bar()
{
Transceiver<bool> t;
bool msg = true;
t.emitMessage(msg);
}
// directed dispatch example
class MyEmitter
{
public:
//...
Transceiver<bool> & getEmitter()
{
return m_emitter;
}
void bar()
{
bool msg = true;
m_emitter.emitMessage(msg);
}
private:
Transceiver<bool> m_emitter;
};
class MyReceiver
{
public:
explicit MyReceiver(MyEmitter & source)
{
m_callback = new Callback;
m_callback->connect(source, *this, &MyReceiver::foo);
}
~MyReceiver()
{
delete m_callback;
}
protected:
void foo(bool)
{
// I received a bool message from one of
// the transceivers I am connected too!
}
};
// anywhere else in the application
void doStuff()
{
MyEmitter emitter;
MyReceiver receiver(emitter);
// receiver.foo(true) is called by way of emitter.bar()
emitter.bar();
}
*/
//-----------------------------------------------------------------------
#include <set>
#include <typeinfo>
#include <vector>
#if _MSC_VER < 1300
using std::type_info;
#endif
//---------------------------------------------------------------------
namespace MessageDispatch {
class TransceiverBase
{
public:
TransceiverBase();
virtual ~TransceiverBase();
protected:
struct GlobalReceiverInfo
{
GlobalReceiverInfo();
std::vector<TransceiverBase *> receivers;
std::vector<TransceiverBase *> pendingAdds;
std::vector<TransceiverBase *> pendingRemoves;
bool locked;
};
protected:
static GlobalReceiverInfo & getGlobalReceiverInfo(const type_info & typeId);
protected:
mutable bool locked;
};
template<typename MessageType, typename IdentifierType = void *>
class Transceiver : public TransceiverBase
{
public:
Transceiver();
virtual ~Transceiver();
void emitMessage(MessageType source) const;
protected:
friend class Callback;
void listenForAny();
//void listenTo(Transceiver<MessageType, IdentifierType> & source);
virtual void receiveMessage(MessageType) {};
private:
Transceiver(const Transceiver & source);
Transceiver & operator = (const Transceiver & rhs);
void addReceiver(Transceiver * target);
void removeReceiver(Transceiver * target);
private:
std::vector<Transceiver<MessageType, IdentifierType> *> localReceivers;
std::set<Transceiver<MessageType, IdentifierType> *> listenSet;
// bookkeeping containers and flags to permit safe usage during
// message emission
mutable std::vector<Transceiver<MessageType, IdentifierType> *> pendingAdds;
mutable std::vector<Transceiver<MessageType, IdentifierType> *> pendingRemoves;
};
//---------------------------------------------------------------------
template<typename MessageType, typename IdentifierType>
inline Transceiver<MessageType, IdentifierType>::Transceiver() :
TransceiverBase(),
localReceivers(),
listenSet(),
pendingAdds(),
pendingRemoves()
{
// ensure the global receiver for this type of transceiver
// is fully constructed before this object (which may be the
// first transceiver) is fully constructed. The global
// receiver info is a static with linkage in Transceiver.cpp,
// and should be destroyed only after ALL transceivers of this
// type have been destroyed.
getGlobalReceiverInfo(typeid(this));
}
//---------------------------------------------------------------------
template<typename MessageType, typename IdentifierType>
inline Transceiver<MessageType, IdentifierType>::~Transceiver()
{
typename std::vector<Transceiver<MessageType, IdentifierType> *>::iterator i;
typename std::set<Transceiver<MessageType, IdentifierType> *>::iterator f;
// advise anyone that this transceiver is connected to that
// they should NOT call removeReceiver in their destructor
// because THIS transceiver is being destroyed
for(i = localReceivers.begin(); i != localReceivers.end(); ++i)
{
// find this receiver on the other transceiver's
// listen set
f = (*i)->listenSet.find(this);
// if it's found, remove this receiver from the listenSet
if(f != (*i)->listenSet.end())
(*i)->listenSet.erase(f);
}
// for every transceiver that this object receives messages
// from, remove thiis object from their distribution
// list, so that they will not attempt to deliver
// messages to this object after it has been destroyed
for(f = listenSet.begin(); f != listenSet.end(); ++f)
{
(*f)->removeReceiver(this);
}
// remove this transceiver from the global receiver list
// to prevent anonymous distribution to this delieted
// transceiver
GlobalReceiverInfo & info = getGlobalReceiverInfo(typeid(this));
std::vector<TransceiverBase *>::iterator g;
if(info.locked == false)
{
g = std::find(info.receivers.begin(), info.receivers.end(), this);
if(g != info.receivers.end())
info.receivers.erase(g);
}
else
{
// the global transceiver list is busy dispatching
// a message that this transceiver may receive, put
// it on the global pending remove list, which is
// checked during message dispatch
info.pendingRemoves.push_back(this);
}
}
//-----------------------------------------------------------------------
template<typename MessageType, typename IdentifierType>
inline void Transceiver<MessageType, IdentifierType>::addReceiver(Transceiver<MessageType, IdentifierType> * target)
{
// do not add a transceiver to the receiver list if this transceiver
// is busy dispatching a message.
if(locked)
{
pendingAdds.push_back(target);
}
else
{
typename std::vector<Transceiver<MessageType, IdentifierType> *>::const_iterator f = std::find(localReceivers.begin(), localReceivers.end(), target);
if(f == localReceivers.end())
{
localReceivers.push_back(target);
target->listenSet.insert(this);
}
}
}
//---------------------------------------------------------------------
template<typename MessageType, typename IdentifierType>
inline void Transceiver<MessageType, IdentifierType>::emitMessage(MessageType message) const
{
//-----------------------------------------------------------------------
// set our status to locked so that adds to this receiver do not touch
// the localReceiverSet and put it in an undefined state.
// See Transceiver::addReceiver for information on the locked flag
locked = true;
// iterate through direct connections to this Transceiver
typename std::vector<Transceiver<MessageType, IdentifierType> *>::const_iterator i;
// which dispatch loop will we run?
// fast dispatch
for(i = localReceivers.begin(); i != localReceivers.end(); ++i)
{
// quick and easy check.
// if no removes have been posted as a result of the
// last dispatch, simply deliver the message
if(pendingRemoves.empty())
{
(*i)->receiveMessage(message);
}
else
{
// it might not be safe, because a transceiver requested a removal. This
// can happen when the owner object of the transceiver has
// deleted itself. Just skip this target and clean up the
// removal at the end of emitMessage.
if(std::find(pendingRemoves.begin(), pendingRemoves.end(), (*i)) != pendingRemoves.end())
continue;
(*i)->receiveMessage(message);
}
}
locked = false;
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// lock the global receiver set to prevent
// unsafe add/remove calls to it while dispatching this message.
GlobalReceiverInfo & info = getGlobalReceiverInfo(typeid(const_cast<Transceiver<MessageType, IdentifierType> *>(this)));
info.locked = true;
std::vector<TransceiverBase *>::iterator g;
for(g = info.receivers.begin(); g != info.receivers.end(); ++g)
{
// quick and easy check.
// if no removes have been posted as a result of the
// last dispatch, simply deliver the message
if(pendingRemoves.empty())
{
Transceiver<MessageType, IdentifierType> * t = static_cast<Transceiver<MessageType, IdentifierType> *>((*g));
t->receiveMessage(message);
}
else
{
// it might not be safe, because a transceiver requested a removal. This
// can happen when the owner object of the transceiver has
// deleted itself. Just skip this target and clean up the
// removal at the end of emitMessage.
if(std::find(info.pendingRemoves.begin(), info.pendingRemoves.end(), (*g)) != info.pendingRemoves.end())
continue;
Transceiver<MessageType, IdentifierType> * t = static_cast<Transceiver<MessageType, IdentifierType> *>((*g));
t->receiveMessage(message);
}
}
info.locked = false;
//-----------------------------------------------------------------------
// process local connections to this transceiver
if(! pendingAdds.empty())
{
for(i = pendingAdds.begin(); i != pendingAdds.end(); ++i)
{
// place the pending transceive ron the receiver list
const_cast<Transceiver<MessageType, IdentifierType> *>(this)->addReceiver(*i);
}
pendingAdds.clear();
}
// a transceiver directly connected to this object was destroyed or
// disconnected during the message dispatch. Remove it from the
// list of receivers to ensure messages aren't delivered to the target
// and that it will not receive a notification during the destruction
// of THIS transceiver
if(! pendingRemoves.empty())
{
typename std::vector<Transceiver<MessageType, IdentifierType> *>::const_iterator a;;
for(a = pendingRemoves.begin(); a != pendingRemoves.end(); ++a)
{
const_cast<Transceiver<MessageType, IdentifierType> *>(this)->removeReceiver(*a);
}
pendingRemoves.clear();
}
// perform the same add/remove logic for the global receiver info
if(! info.pendingAdds.empty())
{
std::vector<TransceiverBase *>::const_iterator addIter;
for(addIter = info.pendingAdds.begin(); addIter != info.pendingAdds.end(); ++addIter)
{
Transceiver<MessageType, IdentifierType> * t = static_cast<Transceiver<MessageType, IdentifierType> *>((*addIter));
t->listenForAny();
}
info.pendingAdds.clear();
}
if(! info.pendingRemoves.empty())
{
std::vector<TransceiverBase *>::const_iterator removeIter;
for(removeIter = info.pendingRemoves.begin(); removeIter != info.pendingRemoves.end(); ++removeIter)
{
std::vector<TransceiverBase *>::iterator r = std::find(info.receivers.begin(), info.receivers.end(), *removeIter);
if(r != info.receivers.end())
info.receivers.erase(r);
}
info.pendingRemoves.clear();
}
}
//---------------------------------------------------------------------
template<typename MessageType, typename IdentifierType>
inline void Transceiver<MessageType, IdentifierType>::listenForAny()
{
GlobalReceiverInfo & info = getGlobalReceiverInfo(typeid(this));
// if the global receiver info is not locked, then it is not
// busy dispatching an anonymous message and it is safe to
// add this transceiver directly to the list of listeners
if(! info.locked)
{
std::vector<TransceiverBase *>::const_iterator f = std::find(info.receivers.begin(), info.receivers.end(), this);
if(f == info.receivers.end())
info.receivers.push_back(this);
}
else
{
// another transceiver of this type is performing
// operations on the global receiver set. Adds will
// be processed by that transceiver when it is done
// working with the global receiver list
info.pendingAdds.push_back(this);
}
}
//-----------------------------------------------------------------------
template<typename MessageType, typename IdentifierType>
inline void Transceiver<MessageType, IdentifierType>::removeReceiver(Transceiver<MessageType, IdentifierType> * target)
{
if(locked)
{
// this transceiver is busy dispatching messages. Do not invalidate
// the receiver list by erasing a member while this object is
// iterating through it to dispatch. The remove list, if it is
// not empty, will be checked to see if the target transceiver
// is listed, and messages will not be dispatched to it.
pendingRemoves.push_back(target);
}
else
{
// it is safe to directly remove the target transceiver from the
// receiver list.
typename std::vector<Transceiver<MessageType, IdentifierType> *>::iterator f = std::find(localReceivers.begin(), localReceivers.end(), target);
if(f != localReceivers.end())
localReceivers.erase(f);
}
}
//---------------------------------------------------------------------
/**
An OwnedTransceiver is a helper/interface class that links end-point
objects, Callback objects and transceivers. The abstraction is
present to make message dispatch safe when emitting messages to objects
that may be deleted. Callbacks and OwnedTransceivers notify other
transceivers if an object is being destroyed. Objects that receive
messages have Callback members, and when those callback members
are destroyed, they clean up transceiver receive lists to prevent
operations on the deleted object.
*/
template<typename MessageType, typename ObjectType, typename IdentifierType = void *>
class OwnedTransceiver : public Transceiver<MessageType, IdentifierType>
{
public:
OwnedTransceiver(Transceiver<MessageType, IdentifierType> * source, ObjectType & o);
~OwnedTransceiver();
// this where all messages are routed before finally being
// delivered to the object member function specified in the
// callback. receiveMessage overrides the base Transceiver method.
virtual void receiveMessage(MessageType msg)
{
(owner.*callback)(msg);
};
void setCallback(void (ObjectType::*cb)(MessageType) )
{
callback = cb;
};
private:
friend class Callback;
OwnedTransceiver<MessageType, ObjectType, IdentifierType> & operator = (const OwnedTransceiver<MessageType, ObjectType, IdentifierType> &);
OwnedTransceiver(const OwnedTransceiver<MessageType, ObjectType, IdentifierType> &);
Transceiver<MessageType, IdentifierType> * source;
ObjectType & owner;
void (ObjectType::*callback)(MessageType);
};
//---------------------------------------------------------------------
template<typename MessageType, typename ObjectType, typename IdentifierType>
inline OwnedTransceiver<MessageType, ObjectType, IdentifierType>::OwnedTransceiver(Transceiver<MessageType, IdentifierType> * s, ObjectType & o) :
source(s),
owner(o),
callback(0)
{
}
//-----------------------------------------------------------------------
template<typename MessageType, typename ObjectType, typename IdentifierType>
inline OwnedTransceiver<MessageType, ObjectType, IdentifierType>::~OwnedTransceiver()
{
}
//-----------------------------------------------------------------------
class Callback
{
public:
Callback();
virtual ~Callback();
template<typename MessageType, typename ObjectType, typename IdentifierType>
void connect(
Transceiver<MessageType, IdentifierType> & source,
ObjectType & object,
void (ObjectType::*callback)(MessageType)
)
{
OwnedTransceiver<MessageType, ObjectType, IdentifierType> * target = new OwnedTransceiver<MessageType, ObjectType, IdentifierType>(&source, object);
receivers.push_back(target);
target->setCallback(callback);
source.addReceiver(target);
//target->listenTo(source);
}
template<typename MessageType, typename ObjectType>
inline void connect(ObjectType & object, void (ObjectType::*callback)(MessageType))
{
OwnedTransceiver<MessageType, ObjectType> * target = new OwnedTransceiver<MessageType, ObjectType>(0, object);
receivers.push_back(target);
target->setCallback(callback);
target->listenForAny();
}
template<typename MessageType, typename ObjectType, typename IdentifierType>
inline void connect(ObjectType & object, void (ObjectType::*callback)(MessageType), IdentifierType *)
{
OwnedTransceiver<MessageType, ObjectType, IdentifierType> * target = new OwnedTransceiver<MessageType, ObjectType, IdentifierType>(0, object);
receivers.push_back(target);
target->setCallback(callback);
target->listenForAny();
}
template<typename MessageType, typename ObjectType, typename IdentifierType>
void disconnect(
Transceiver<MessageType, IdentifierType> & source,
ObjectType & object,
void (ObjectType::*callback)(MessageType)
)
{
removeTarget(&source, object, callback);
}
template<typename MessageType, typename ObjectType, typename IdentifierType>
void disconnectByType(
Transceiver<MessageType, IdentifierType> & source,
ObjectType & object,
void (ObjectType::*callback)(MessageType),
IdentifierType *
)
{
removeTarget(&source, object, callback);
}
template<typename MessageType, typename ObjectType>
void disconnect(ObjectType & object, void (ObjectType::*callback)(MessageType) )
{
removeTarget(static_cast<Transceiver<MessageType> *>(0), object, callback);
}
template<typename MessageType, typename ObjectType, typename IdentifierType>
void disconnect(ObjectType & object, void (ObjectType::*callback)(MessageType), IdentifierType *)
{
removeTarget(static_cast<Transceiver<MessageType, IdentifierType> *>(0), object, callback);
}
private:
template<typename MessageType, typename ObjectType, typename IdentifierType>
void removeTarget(
Transceiver<MessageType, IdentifierType> * source,
ObjectType & object,
void (ObjectType::*callback)(MessageType)
)
{
std::vector<TransceiverBase *>::iterator i;
OwnedTransceiver<MessageType, ObjectType, IdentifierType> * target;
for(i = receivers.begin(); i != receivers.end(); ++i)
{
target = dynamic_cast<OwnedTransceiver<MessageType, ObjectType, IdentifierType> *>((*i));
if(target)
{
if(&target->owner == &object && target->callback == callback && target->source == source)
{
delete target;
receivers.erase(i);
break;
}
}
}
}
std::vector<TransceiverBase *> receivers;
};
template<typename MessageType>
void emitMessage(MessageType m)
{
static Transceiver<MessageType> t;
t.emitMessage(m);
}
//-----------------------------------------------------------------------
}//namespace MessageDispatch
#endif // _Transceiver_H
@@ -0,0 +1,8 @@
// ======================================================================
//
// FirstMessageDispatch.cpp
// copyright (c) 2001 Sony Online Entertainment
//
// ======================================================================
#include "sharedMessageDispatch/FirstSharedMessageDispatch.h"