IdentifierFamily


#1

This is quite a niche thing, but I figured I’d share it here in case anyone else had the same idea at any stage…

It’s basically Jules’ Identifier code in a template form (the code is copy/pasted, as evidenced by the comments!). The template parameter must be a class with a static getIdentifierPool() function to access a static StringPool. Instead of Identifier, you can use IdentifierFamily, which uses its own pool.

There is a macro to make this easier, which also creates a handy typedef (you don’t need to use the macro, but it saves effort!).

Example

class SomeType
{
public:
   ...
   // This macro just gives the class a static getIdentifierPool() accessor and creates a typedef
   // SomeType is this class name, Id is the desired typedef name for IdentifierFamily<SomeType>
   DECLARE_IDENTIFIERFAMILY_TYPE(SomeType,Id);

};
...
SomeType::Id id("Foo");

The reason I wrote it is that I have various different types of object which each make use of an Identifier. These object types exist in varying quantities (some have a huge number of different instances, others have very few). If they all used the same pool, then the Identifier creation overhead would always be bound by the overall number of unique Identifier strings in the application. As misfortune would have it, some of the less-common types I’m using will regularly face the Identifier creation overhead (dealing with a String from script). As the types I’ll be dealing with do not need their identifiers to be interchangeable, there is potential for a quicker table check if each type uses its own pool (at the obvious expense of memory from duplicates across the different families).

Here’s the code for a header file.

template <class FamilyType>
class IdentifierFamily
{
public:
	
	/** Creates a null identifier. */
	IdentifierFamily() noexcept
	:	name(nullptr)
	{
	}
	
	/** Creates an identifier with a specified name.
	 Because this name may need to be used in contexts such as script variables or XML
	 tags, it must only contain ascii letters and digits, or the underscore character.
	 */
	IdentifierFamily (const char* name_)
	:	name (getPool().getPooledString(name_))
	{
		/* An Identifier string must be suitable for use as a script variable or XML
		 attribute, so it can only contain this limited set of characters.. */
		jassert (isValidIdentifier (name_));
	}
	
	/** Creates an identifier with a specified name.
	 Because this name may need to be used in contexts such as script variables or XML
	 tags, it must only contain ascii letters and digits, or the underscore character.
	 */
	IdentifierFamily (const String& name_)
	:	name (getPool().getPooledString(name_))
	{
		/* An Identifier string must be suitable for use as a script variable or XML
		 attribute, so it can only contain this limited set of characters.. */
		jassert (isValidIdentifier (name_));
	}
	
	/** Creates a copy of another identifier. */
	IdentifierFamily (const IdentifierFamily& other) noexcept
	:	name (other.name)
	{
	}
	
	/** Creates a copy of another identifier. */
	IdentifierFamily& operator= (const IdentifierFamily& other) noexcept
	{
		name = other.name;
		return *this;
	}
	
	/** Destructor */
	~IdentifierFamily()
	{
	}
	
	/** Compares two identifiers. This is a very fast operation. */
	inline bool operator== (const IdentifierFamily& other) const noexcept     { return name == other.name; }
	
	/** Compares two identifiers. This is a very fast operation. */
	inline bool operator!= (const IdentifierFamily& other) const noexcept     { return name != other.name; }
	
	/** Returns this identifier as a string. */
	String toString() const                                             { return name; }
	
	/** Returns this identifier's raw string pointer. */
	operator const String::CharPointerType() const noexcept             { return name; }
	
	/** Checks a given string for characters that might not be valid in an Identifier.
	 Since Identifiers are used as a script variables and XML attributes, they should only contain
	 alphanumeric characters, underscores, or the '-' and ':' characters.
	 */
	static bool isValidIdentifier (const String& possibleIdentifier) noexcept
	{
		return possibleIdentifier.isNotEmpty()
		&& possibleIdentifier.containsOnly ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:#@$%");
	}
	
	
private:
	
	//==============================================================================
	String::CharPointerType name;
	
	static StringPool& getPool()
	{
		return FamilyType::getIdentifierPool();
	}
	
};


#define DECLARE_IDENTIFIERFAMILY_TYPE(FamilyType,IdClassName) \
\
public: \
\
	typedef IdentifierFamily< FamilyType > IdClassName; \
\
private: \
\
	friend class IdentifierFamily< FamilyType >; \
\
	static StringPool& getIdentifierPool () \
	{ \
		static StringPool pool; \
		return pool; \
	} \
\

#2

Nice!