Skip to content

Commit

Permalink
Refactor|libdeng2: Pimpl-friendly audiences
Browse files Browse the repository at this point in the history
Defined a new set of audience macros that can be used in a pimpl
friendly manner, i.e., without public member variables. They require
adding accessor methods for the audiences, though (using new macros).

Applied the pimpl idiom to Asset, Clock, File, and Variable.
  • Loading branch information
skyjake committed Mar 2, 2014
1 parent bc791f9 commit 90209ed
Show file tree
Hide file tree
Showing 37 changed files with 457 additions and 212 deletions.
6 changes: 3 additions & 3 deletions doomsday/libdeng2/include/de/core/app.h
Expand Up @@ -69,17 +69,17 @@ class DENG2_PUBLIC App : DENG2_OBSERVES(Clock, TimeChange)
/**
* Notified when application startup has been fully completed.
*/
DENG2_DEFINE_AUDIENCE(StartupComplete, void appStartupCompleted())
DENG2_DEFINE_AUDIENCE2(StartupComplete, void appStartupCompleted())

/**
* Notified before the current game is unloaded.
*/
DENG2_DEFINE_AUDIENCE(GameUnload, void aboutToUnloadGame(game::Game const &gameBeingUnloaded))
DENG2_DEFINE_AUDIENCE2(GameUnload, void aboutToUnloadGame(game::Game const &gameBeingUnloaded))

/**
* Notified after the current game has been changed.
*/
DENG2_DEFINE_AUDIENCE(GameChange, void currentGameChanged(game::Game const &newGame))
DENG2_DEFINE_AUDIENCE2(GameChange, void currentGameChanged(game::Game const &newGame))

public:
/**
Expand Down
7 changes: 4 additions & 3 deletions doomsday/libdeng2/include/de/core/asset.h
Expand Up @@ -51,15 +51,16 @@ class DENG2_PUBLIC Asset
/**
* Notified whenever the state of the asset changes.
*/
DENG2_DEFINE_AUDIENCE(StateChange, void assetStateChanged(Asset &))
DENG2_DEFINE_AUDIENCE2(StateChange, void assetStateChanged(Asset &))

/**
* Notified when the asset is destroyed.
*/
DENG2_DEFINE_AUDIENCE(Deletion, void assetDeleted(Asset &))
DENG2_DEFINE_AUDIENCE2(Deletion, void assetDeleted(Asset &))

public:
Asset(State initialState = NotReady);
Asset(Asset const &other);
virtual ~Asset();

void setState(State s);
Expand All @@ -72,7 +73,7 @@ class DENG2_PUBLIC Asset
virtual bool isReady() const;

private:
State _state;
DENG2_PRIVATE(d)
};

/**
Expand Down
5 changes: 2 additions & 3 deletions doomsday/libdeng2/include/de/core/clock.h
Expand Up @@ -36,7 +36,7 @@ class DENG2_PUBLIC Clock
* Notified whenever the time of the clock changes. The audience members
* will be notified in unspecified order.
*/
DENG2_DEFINE_AUDIENCE(TimeChange, void timeChanged(Clock const &))
DENG2_DEFINE_AUDIENCE2(TimeChange, void timeChanged(Clock const &))

/**
* Notified whenever the time of the clock changes. The entire priority
Expand Down Expand Up @@ -73,8 +73,7 @@ class DENG2_PUBLIC Clock
static Time const &appTime();

private:
Time _startedAt;
Time _time;
DENG2_PRIVATE(d)

static Clock *_appClock;
};
Expand Down
2 changes: 1 addition & 1 deletion doomsday/libdeng2/include/de/core/loop.h
Expand Up @@ -39,7 +39,7 @@ class DENG2_PUBLIC Loop : public QObject
/**
* Audience to be notified each time the loop iterates.
*/
DENG2_DEFINE_AUDIENCE(Iteration, void loopIteration())
DENG2_DEFINE_AUDIENCE2(Iteration, void loopIteration())

public:
/**
Expand Down
4 changes: 2 additions & 2 deletions doomsday/libdeng2/include/de/data/bank.h
Expand Up @@ -176,13 +176,13 @@ class DENG2_PUBLIC Bank
* Notified when a data item has been loaded to memory (cache level
* InMemory). May be called from the background thread, if one is running.
*/
DENG2_DEFINE_AUDIENCE(Load, void bankLoaded(DotPath const &path))
DENG2_DEFINE_AUDIENCE2(Load, void bankLoaded(DotPath const &path))

/**
* Notified when a data item's cache level changes (in addition to the Load
* notification).
*/
DENG2_DEFINE_AUDIENCE(CacheLevel, void bankCacheLevelChanged(DotPath const &path, CacheLevel level))
DENG2_DEFINE_AUDIENCE2(CacheLevel, void bankCacheLevelChanged(DotPath const &path, CacheLevel level))

public:
/**
Expand Down
4 changes: 2 additions & 2 deletions doomsday/libdeng2/include/de/data/escapeparser.h
Expand Up @@ -38,15 +38,15 @@ class DENG2_PUBLIC EscapeParser
*
* @param range Range in the original text.
*/
DENG2_DEFINE_AUDIENCE(PlainText, void handlePlainText(Rangei const &range))
DENG2_DEFINE_AUDIENCE2(PlainText, void handlePlainText(Rangei const &range))

/**
* Called during parsing when an escape sequence has been parsed.
* Does not include the Esc (0x1b) in the beginning.
*
* @param range Range in the original text.
*/
DENG2_DEFINE_AUDIENCE(EscapeSequence, void handleEscapeSequence(Rangei const &range))
DENG2_DEFINE_AUDIENCE2(EscapeSequence, void handleEscapeSequence(Rangei const &range))

public:
EscapeParser();
Expand Down
72 changes: 72 additions & 0 deletions doomsday/libdeng2/include/de/data/observers.h
Expand Up @@ -61,6 +61,24 @@
typedef de::Observers<DENG2_AUDIENCE_INTERFACE(Name)> Name##Audience; \
extern Name##Audience audienceFor##Name;

#define DENG2_DECLARE_AUDIENCE_METHOD(Name) \
typedef de::Observers<DENG2_AUDIENCE_INTERFACE(Name)> Name##Audience; \
Name##Audience &audienceFor##Name(); \
Name##Audience const &audienceFor##Name() const;

#define DENG2_AUDIENCE_METHOD(ClassName, Name) \
ClassName::Name##Audience &ClassName::audienceFor##Name() { return d->audienceFor##Name; } \
ClassName::Name##Audience const &ClassName::audienceFor##Name() const { return d->audienceFor##Name; }

#define DENG2_AUDIENCE_METHOD_INLINE(Name) \
typedef de::Observers<DENG2_AUDIENCE_INTERFACE(Name)> Name##Audience; \
Name##Audience _audienceFor##Name; \
Name##Audience &audienceFor##Name() { return _audienceFor##Name; } \
Name##Audience const &audienceFor##Name() const { return _audienceFor##Name; }

#define DENG2_PIMPL_AUDIENCE(Name) \
Name##Audience audienceFor##Name;

/**
* Macro for defining an observer interface containing a single method.
*
Expand All @@ -73,6 +91,14 @@
DENG2_DECLARE_AUDIENCE(Name, Method) \
DENG2_AUDIENCE(Name)

#define DENG2_DEFINE_AUDIENCE2(Name, Method) \
DENG2_DECLARE_AUDIENCE(Name, Method) \
DENG2_DECLARE_AUDIENCE_METHOD(Name)

#define DENG2_DEFINE_AUDIENCE_INLINE(Name, Method) \
DENG2_DECLARE_AUDIENCE(Name, Method) \
DENG2_AUDIENCE_METHOD_INLINE(Name)

/**
* Macro that can be used in class declarations to specify which audiences the class
* can belong to.
Expand Down Expand Up @@ -101,6 +127,9 @@
#define DENG2_FOR_AUDIENCE(Name, Var) \
DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, audienceFor##Name)

#define DENG2_FOR_AUDIENCE2(Name, Var) \
DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, audienceFor##Name())

/**
* Macro for looping through the public audience members from inside a private
* implementation.
Expand All @@ -111,12 +140,55 @@
#define DENG2_FOR_PUBLIC_AUDIENCE(Name, Var) \
DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, self.audienceFor##Name)

#define DENG2_FOR_PUBLIC_AUDIENCE2(Name, Var) \
DENG2_FOR_EACH_OBSERVER(Name##Audience, Var, audienceFor##Name)

namespace de {

/**
* Template for observer sets. The template type should be an interface
* implemented by all the observers. @ingroup data
*
* @par How to use the non-pimpl audience macros
*
* These examples explain how to create an audience called "Deletion". In general,
* audience names should be nouns like this so they can be used in the form
* "audience for (something)".
*
* In a class declaration, define the audience in the @c public section of the class:
* <pre>DENG2_DEFINE_AUDIENCE(Deletion, ...interface-function...)</pre>
*
* This will generate a public member variable called @c audienceForDeletion that
* can be directly manipulated by other objects.
*
* Note that because the audience is created as a public member variable, this can
* easily lead to ABI backwards compatibility issues down the road if there is
* need to make changes to the class.
*
* @par How to use the pimpl audience macros
*
* Another set of macros is provided for declaring and defining a pimpl-friendly
* audience. The caveat is that you'll need to separately declare accessor methods
* and define the audience inside the private implementation of the class.
*
* First, define the audience in the @c public section of the class:
* <pre>DENG2_DEFINE_AUDIENCE2(Deletion, ...interface-function...)</pre>
*
* This works like DENG2_DEFINE_AUDIENCE, but without a public member variable.
* Instead, accessor methods are declared for accessing the audience.
*
* Then, inside the private implementation (@c Instance struct), define the audience:
* <pre>DENG2_PIMPL_AUDIENCE(Deletion)</pre>
*
* Finally, define the accessor methods (for instance, just before the constructor
* of the class):
* <pre>DENG2_AUDIENCE_METHOD(ClassName, Deletion)</pre>
*
* It is recommended to keep the DENG2_PIMPL_AUDIENCE and DENG2_AUDIENCE_METHOD
* macros close together in the source file for easier maintenance. The former
* could be at the end of the @c Instance struct while the latter is immediately
* following the struct.
*
* @par Thread-safety
*
* Observers and Observers::Loop lock the observer set separately for reading
Expand Down
4 changes: 2 additions & 2 deletions doomsday/libdeng2/include/de/data/property.h
Expand Up @@ -69,12 +69,12 @@ class BaseProperty
void setValue(ValueType const &v) { \
if(_value == v) return; \
_value = v; \
DENG2_FOR_AUDIENCE(Change, i) i->valueOf ## PropName ## Changed(); \
DENG2_FOR_AUDIENCE2(Change, i) i->valueOf ## PropName ## Changed(); \
} \
PropName &operator = (ValueType const &v) { setValue(v); return *this; } \
PropName &operator += (ValueType const &v) { setValue(_value + v); return *this; } \
PropName &operator -= (ValueType const &v) { setValue(_value - v); return *this; } \
DENG2_DEFINE_AUDIENCE(Change, void valueOf ## PropName ## Changed()) \
DENG2_DEFINE_AUDIENCE_INLINE(Change, void valueOf ## PropName ## Changed()) \
};

#define DENG2_PROPERTY(PropName, ValueType) \
Expand Down
2 changes: 1 addition & 1 deletion doomsday/libdeng2/include/de/data/record.h
Expand Up @@ -63,7 +63,7 @@ class DENG2_PUBLIC Record : public ISerializable, public LogEntry::Arg::Base,
typedef std::pair<String, String> KeyValue;
typedef QList<KeyValue> List;

DENG2_DEFINE_AUDIENCE(Deletion, void recordBeingDeleted(Record &record))
DENG2_DEFINE_AUDIENCE2(Deletion, void recordBeingDeleted(Record &record))

public:
Record();
Expand Down
21 changes: 9 additions & 12 deletions doomsday/libdeng2/include/de/data/variable.h
Expand Up @@ -126,7 +126,7 @@ class DENG2_PUBLIC Variable : public ISerializable
/**
* Returns the name of the variable.
*/
String const &name() const { return _name; }
String const &name() const;

/**
* Sets the value of the variable.
Expand Down Expand Up @@ -159,12 +159,15 @@ class DENG2_PUBLIC Variable : public ISerializable
*/
Value &value();

Value *valuePtr();
Value const *valuePtr() const;

/**
* Returns the value of the variable.
*/
template <typename Type>
Type &value() {
Type *v = dynamic_cast<Type *>(_value);
Type *v = dynamic_cast<Type *>(valuePtr());
if(!v) {
/// @throw TypeError Casting to Type failed.
throw TypeError("Variable::value",
Expand Down Expand Up @@ -193,7 +196,7 @@ class DENG2_PUBLIC Variable : public ISerializable
*/
template <typename Type>
Type const &value() const {
Type const *v = dynamic_cast<Type const *>(_value);
Type const *v = dynamic_cast<Type const *>(valuePtr());
if(!v) {
/// @throw TypeError Casting to Type failed.
throw TypeError("Variable::value",
Expand Down Expand Up @@ -265,24 +268,18 @@ class DENG2_PUBLIC Variable : public ISerializable
*
* @param variable Variable.
*/
DENG2_DEFINE_AUDIENCE(Deletion, void variableBeingDeleted(Variable &variable))
DENG2_DEFINE_AUDIENCE2(Deletion, void variableBeingDeleted(Variable &variable))

/**
* The value of the variable has changed.
*
* @param variable Variable.
* @param newValue New value of the variable.
*/
DENG2_DEFINE_AUDIENCE(Change, void variableValueChanged(Variable &variable, Value const &newValue))
DENG2_DEFINE_AUDIENCE2(Change, void variableValueChanged(Variable &variable, Value const &newValue))

private:
String _name;

/// Value of the variable.
Value *_value;

/// Mode flags.
Flags _mode;
DENG2_PRIVATE(d)
};

Q_DECLARE_OPERATORS_FOR_FLAGS(Variable::Flags)
Expand Down
36 changes: 8 additions & 28 deletions doomsday/libdeng2/include/de/filesys/file.h
Expand Up @@ -82,7 +82,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream
*
* @param file The file object being deleted.
*/
DENG2_DEFINE_AUDIENCE(Deletion, void fileBeingDeleted(File const &file))
DENG2_DEFINE_AUDIENCE2(Deletion, void fileBeingDeleted(File const &file))

/**
* Stores the status of a file (size, time of last modification).
Expand Down Expand Up @@ -187,7 +187,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream
static FileSystem &fileSystem();

/// Returns the name of the file.
String const &name() const { return _name; }
String const &name() const;

/**
* Returns a textual description of the file, intended only for humans.
Expand All @@ -210,12 +210,12 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream
/**
* Sets the parent folder of this file.
*/
void setParent(Folder *parent) { _parent = parent; }
void setParent(Folder *parent);

/**
* Returns the parent folder. May be NULL.
*/
Folder *parent() const { return _parent; }
Folder *parent() const;

/**
* Sets the origin Feed of the File. The origin feed is the feed that is able
Expand All @@ -233,7 +233,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream
* Returns the origin Feed of the File.
* @see setOriginFeed()
*/
Feed *originFeed() const { return _originFeed; }
Feed *originFeed() const;

/**
* Sets the source file of this file. The source is where this file is
Expand Down Expand Up @@ -306,10 +306,10 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream
virtual void setMode(Flags const &newMode);

/// Returns the file information (const).
Record const &info() const { return _info; }
Record const &info() const;

/// Returns the file information.
Record &info() { return _info; }
Record &info();

/**
* Makes sure that the file has write access.
Expand All @@ -333,27 +333,7 @@ class DENG2_PUBLIC File : public Lockable, public IIOStream
explicit File(String const &name = "");

private:
/// The parent folder.
Folder *_parent;

/// The source file (NULL for non-interpreted files).
File *_source;

/// Feed that generated the file. This feed is called upon when the file needs
/// to be pruned. May also be NULL.
Feed *_originFeed;

/// Name of the file.
String _name;

/// Status of the file.
Status _status;

/// Mode flags.
Flags _mode;

/// File information.
Record _info;
DENG2_PRIVATE(d)
};

Q_DECLARE_OPERATORS_FOR_FLAGS(File::Flags)
Expand Down

0 comments on commit 90209ed

Please sign in to comment.