#pragma once
/*
Provides these macros:
BEGIN_NAMESPACE
END_NAMESPACE
USING_NAMESPACE
ARRAY_SIZE
STATIC_ASSERT
ENUM_AS_FLAGS
DEBUG_PRINT
Provides these types:
Size
Point
Rectangle
SharedPtr
WeakPtr
Vector
Map
HashMap
Pair
BidirectionalMap
String
Provides these functions:
clamp
lerp
*/
#include <string>
#include <vector>
#include <map>
#include <mutex>
#include <unordered_map>
#include <cstdint> // uint32_t etc
#include <memory> // shared_ptr
// Potentially using a namespace could produce larger binaries due to
// symbols containing the namespace, therefore for embedded or memory
// constrained systems, these defines make it possible to easily change
// this and compile without the APIs being contained in a namespace
#ifndef NO_NAMESPACE
# define NAMESPACE Lute
# define BEGIN_NAMESPACE namespace NAMESPACE {
# define END_NAMESPACE };
# define USING_NAMESPACE using namespace NAMESPACE; // Avoid, as this can break unity/compilation-unit builds
#else
# define NAMESPACE
# define BEGIN_NAMESPACE
# define END_NAMESPACE
# define USING_NAMESPACE
#endif
// Features
#define USE_RETINA 1
#ifdef USE_RETINA
// const bool c_useRetina = true;
extern bool c_useRetina;
extern float c_retinaScale;
#endif
// Emulate C++17 feature
namespace std
{
template <class C>
constexpr auto size(const C& c) -> decltype(c.size())
{
return c.size();
}
template <class T, std::size_t N>
constexpr std::size_t size(const T (&array)[N]) noexcept
{
return N;
}
}
#define ARRAY_SIZE(array) \
std::size(array)
#define STATIC_ASSERT(condition, name) \
static_assert(condition, #name);
#define ENUM_AS_FLAGS(enum) \
inline enum operator|(enum a, enum b) { return static_cast<enum>(static_cast<int>(a) | static_cast<int>(b)); } \
inline enum operator|=(enum& a, enum b) { return a = static_cast<enum>(static_cast<int>(a) | static_cast<int>(b)); } \
inline enum operator&=(enum& a, enum b) { return a = static_cast<enum>(static_cast<int>(a) & static_cast<int>(b)); } \
inline enum operator~(enum a) { return static_cast<enum>(~static_cast<int>(a)); }
// Probably should use a logger
// Logger context, so all classes / components can get their context
// Some kind of base object type, and objects in a hierarchy, and the root object
// holds the context, has a logger object, and all the objects in the tree have
// pointers to this context and can call the logger (so no singletons or global
// pointers, can have different loggers if have different root objects and contexts)
#define DEBUG_PRINT(fmt, ...) \
fprintf(stderr, "%s(%d): " fmt, __FILE__, __LINE__, __VA_ARGS__)
// Not sure I like this, macros should be deprecated
// However I want to anticipate some upcoming new language features
// that might make it possible to have real interfaces in C++
//#define DECLARE_INTERFACE(interface) \
// class interface : public InterfaceBase
BEGIN_NAMESPACE
template<typename T>
static T clamp(T a_val, T a_min, T a_max)
{
return std::min(std::max(a_val, a_min), a_max);
}
template<typename T>
static T lerp(T v0, T v1, T t)
{
return (T(1) - t) * v0 + t * v1; // Less precise alternative: return v0 + t * (v1 - v0);
}
struct Size
{
int m_width;
int m_height;
};
struct Point
{
int m_x;
int m_y;
};
struct Rectangle
{
union {
Point m_position;
struct
{
int m_x;
int m_y;
};
};
union {
Size m_size;
struct
{
int m_width;
int m_height;
};
};
};
template <typename T>
class SharedPtr : public std::shared_ptr<T>
{
public:
SharedPtr(T* newItem) : std::shared_ptr<T>(newItem) {}
};
template <typename T>
class WeakPtr : public std::weak_ptr<T>
{
};
// http://en.cppreference.com/w/cpp/language/rule_of_three
class PolymorphicClassBase
{
public:
PolymorphicClassBase() = default;
virtual ~PolymorphicClassBase() = default;
PolymorphicClassBase(const PolymorphicClassBase&) = default;
PolymorphicClassBase(PolymorphicClassBase&&) = default;
PolymorphicClassBase& operator=(const PolymorphicClassBase&) = default;
PolymorphicClassBase& operator=(PolymorphicClassBase&&) = default;
};
class InterfaceBase : public PolymorphicClassBase
{
};
class AbstractClassBase : public PolymorphicClassBase
{
};
//
// TODO: need to research std::move and && stuff and idea of moving objects
// and providing appropriate constructors etc.
//
template <typename T>
class Vector : public std::vector<T>
{
public:
Vector() {}
Vector(size_t _Count, const T& _Val) : std::vector<T>(_Count, _Val) {}
};
// ---------------------
// UNICODE SUPPORT
// ---------------------
//
// TODO: 2 String classes, String and UnicodeString ??
// String of bytes - a byte-array or latin-1 string
// UnicodeString - an array of characters (code-points, 32-bits)
//
// Text entry of non-latin-1 text. IME - returns strings in what format?
// requires unit-tests - how to test exactly?
//
// Text comparison and search:
// Some characters (code-points) are combining characters, so in the
// string it says to for example compose together the 2 glyphs of the
// 2 code-points. This is distinct from glyphs which are composite themselves.
// Visually, a single code-point which points to a composite glyph which is
// made up of other glpyhs, may be equivalent to combining characters where
// the multiple code-points combine to build up a visual character. Therefore
// when searching text or comparing text, it may be necessary to normalize
// the text so that visually equivalent text is matched. This will require
// some research to figure out what the equivalent combining characters are
// as code-points.
// Example:
// U+0308 ("¨") is a combining character
// "A" + "¨" (U+0041 U+0308)
// The result is visually the same as:
// "Ä" (U+00C4)
// Not all combinations of combining character with other characters might
// have a code-point, so I guess the normalization process would be to
// de-compose the single code-point version in to characters with combining
// characters. eg: convert U+00C4 in to U+0041 U+0308.
//
// Other issues:
// U+03A9 greek capital letter omega and U+2126 ohm sign
// These both map to the same 'abstract character'. Is there a table for
// this? Why? So when normalizing, do these need to be normalized too?
//
// This might be an argument for converting UTF-8 in to UCS-4 first, and
// additionally normalizing the text in the process. Although similarly,
// UTF-8 could be converted to normalized UTF-8, as the normalization is
// just on the code-points, not the encoding. Depending what is desired
// then, if the UTF-8 was normalized, string comparisons and search can
// proceed as if the text was 8-bit ASCII text just byte searching,
// irrespective of code-points etc. It would though be ignoring if the
// strings to match evenly decode to full code-points or if there are
// partial code-points, but that can easily either be checked or ignored
// depending on the context and source of the inputs.
//
// Cursor movement and selection:
// 'Grapheme cluster' is approximately what is a letter. When code-points
// combine etc, then when you move the cursor left or right, it moves
// by the appropriate amount of code-points to get to the next grapheme
// cluster.
//
// Other issues with this is Arabic and RTL languages. This acts weird
// when RTL and LTR text appears together.
//
// i18n support:
// decimal_point() and thousands_sep() need to return strings, not code-points
// toupper() and tolower() need to work at a string level, not on characters
// in other languages, upper and lower versions may be a different number of
// code-points.
//
// UTF-8 Everywhere Philosophy
// http://utf8everywhere.org/
//
// UTF-8 Pros:
// Still only 4 bytes in worse case to represent all values?? Is that correct??
// Endianess independent
// Some kind of sort preservation - not sure I understand this one.
//
// Slightly less efficient space wise for Asian languages compared to UTF-16,
// however for storage, if compression is used, the difference becomes very small.
// Also if the text contains latin-1 markup like xml or html, the difference
// disappears and before or after compression, utf-8 is better.
//
// Perhaps UnicodeString could be an internal library class.
//
// Here 'http://utf8everywhere.org/' recommends using the W versions
// of win32 APIs and at the point of calling them, widen the utf-8 strings
// to UTF-16. This approach will definitely work. They show example functions
// called widen and narrow as helpers to make this easy.
//
// On recent versions of Windows, possibly using the 'A' version of
// functions can operate on UTF-8 strings with correct locale and
// code-page settings. Ideally, this library won't need to concern
// itself with these text related Win32 APIs other than possibly
// window titles and file names. The goal is that text rendering is
// handled internally. Other issues are with the clip-board and
// drag'n'drop where there is interchange with other apps on the OS.
//
// Discussion:
//
// When reading and writing files or dealing with large data then
// don't need/want the overhead of interpreting the bytes as 32-bits
// but the downside is that the length is the count of bytes, not
// the count of characters. Also in cases where the strings are just
// for debugging, then they may only need to be latin-1.
//
// Advantage of UnicodeStrings is in the case of using for display
// of shorter strings that will be used over again, there is no need
// to decode utf-8 or utf-16 each time, the string can be used more
// directly with the font to display the string.
//
// Debatably, it also means the length of the string is the count of
// characters. This might or might not be too important. Unless the
// font is a fixed width font the correct way to determine the screen
// width of a string is to use a specialized function to calculate the
// size of a string. Even if a fixed-width font apparently with
// 'combining marks' the count won't be enough to work out the screen
// width. The only real advantage then seems to be the caching of
// the string for lookup in to the glyphs for a font.
//
// Unit-tests required
// - need to handle other text-encodings for dealing with the
// file system. The file names could be encoded other than
// utf-8 or utf-16 etc.
//
// Need to unit-test different languages:
// - English, Japanese, Chinese, Thai, Arabic
// - Files with characters of these languages
// - Tests for compound glyphs - the glyph is composed of other glyphs
//
class String : public std::string
{
public:
String() {}
String(const char* fmt, ...)
{
std::vector<char> str(50,'\0');
va_list ap;
int n = -1;
while ((n <= -1) || (size_t(n) >= str.size())) {
str.resize((n <= -1) ? (str.size() * 2) : (n + 1));
va_start(ap, fmt);
n = vsnprintf(str.data(), str.size(), fmt, ap);
va_end(ap);
}
*this = str.data();
}
String(std::string a_str) : std::string(a_str) {}
const std::string& toUtf8() const { return *this; }
String& operator=(std::string a_str) { *((std::string*)this) = a_str; return *this; }
String& operator=(const char* a_str) { *((std::string*)this) = a_str; return *this; }
operator char const*() const { return c_str(); }
};
template <typename T1, typename T2>
class Pair : public std::pair<T1,T2>
{
};
// Map - std::map -> generic, impl using rb-trees
// HashMap - std::unordered_map -> faster, impl uses hashing, so constraints on key
// Dictionary - a HashMap where the key is always a string, potential optimizations
// Cache - a map which is given constraits on how much it can be allowed to grow
template <typename T1, typename T2>
class Map : public std::map<T1,T2>
{
public:
void insert(T1 key, T2 value) { std::map<T1,T2>::insert(std::make_pair(key, value)); }
// T2 getOrAdd(T1 key, Functor createFunction) {}
};
template <typename T1, typename T2>
class HashMap : public std::unordered_map<T1,T2>
{
public:
void insert(T1 key, T2 value) { std::unordered_map<T1,T2>::insert(std::make_pair(key, value)); }
// T2 getOrAdd(T1 key, Functor createFunction) {}
};
template <typename T>
class Dictionary : public std::unordered_map<String,T>
{
public:
void insert(String key, T value) { std::unordered_map<String,T>::insert(std::make_pair(key, value)); }
};
template <typename KeyT, typename ValueT>
class Cache : public std::unordered_map<KeyT, ValueT>
{
public:
// TODO: Need to add functionality to set a policy on the cache behaviour
// eg: it needs to have a cost function as well as a policy on what it
// keeps and what it throws away and functions to make it do this if not transparently.
// map.load_factor() ?? is this C++11 standard
// TODO: perhaps with help of shared_ptr, can track if a value in the cache has
// any active users of it (other than the ref the cache is holding ptr.use_count() )
// - Note there could be threading issues - may require a lock when giving out
// an item from the cache and when trying to free up items from the cache.
};
template <typename T1, typename T2>
class BidirectionalMap
{
public:
size_t size() { return m_forwardMap.size(); }
const T2& forwardFind(const T1& a, const T2& def) const {
return (m_forwardMap.find(a) != m_forwardMap.end()) ? m_forwardMap.find(a)->second : def;
}
const T1& reverseFind(const T2& b, const T1& def) const {
return (m_reverseMap.find(b) != m_reverseMap.end()) ? m_reverseMap.find(b)->second : def;
}
void insert(T1 a, T2 b) {
m_forwardMap.insert(a, b);
m_reverseMap.insert(b, a);
}
private:
Map<T1,T2> m_forwardMap;
Map<T2,T1> m_reverseMap;
};
// Need to use inheritance from unique_lock to make it compatible with using with condition_variables
class ScopeLock : public std::unique_lock<std::recursive_mutex>
{
public:
ScopeLock(std::recursive_mutex& a_mutex) : std::unique_lock<std::recursive_mutex>(a_mutex) {}
template <typename F>
void unlocked(F&& a_func)
{
unlock();
a_func();
lock();
}
};
template <typename T>
class GenericFactoryItem
{
public:
GenericFactoryItem(const char* a_name, T a_value)
: m_name(a_name), m_value(a_value), m_next(getFactoryHead())
{
getFactoryHead(this);
}
static const GenericFactoryItem<T>* getFactoryHead(const GenericFactoryItem<T>* newValue = nullptr);
const char* m_name;
T m_value;
const GenericFactoryItem<T>* m_next;
};
// It shouldn't matter, but for XCode this needed to not be inline in the definition above
template <typename T>
const GenericFactoryItem<T>* GenericFactoryItem<T>::getFactoryHead(const GenericFactoryItem<T>* newValue)
{
static const GenericFactoryItem<T>* s_factoryHead = nullptr;
if (newValue != nullptr)
s_factoryHead = newValue;
return s_factoryHead;
}
template <typename T>
class GenericFactoryIterator
{
public:
GenericFactoryIterator(const GenericFactoryItem<T>* a_value) : m_current(a_value) { }
GenericFactoryIterator<T>& operator++() { m_current = m_current->m_next; return *this; }
bool operator!=(const GenericFactoryIterator<T>& a_other) { return m_current != a_other.m_current; }
const T& operator*() { return m_current->m_value; }
private:
const GenericFactoryItem<T>* m_current;
};
template <typename T>
class GenericFactory
{
public:
GenericFactoryIterator<T> begin() { return GenericFactoryIterator<T>(GenericFactoryItem<T>::getFactoryHead()); }
GenericFactoryIterator<T> end() { return GenericFactoryIterator<T>(nullptr); }
};
/*
// Example usage:
typedef const ReflectionDataInterface* (*ReflectionDataFactory)();
using ReflectionDataFactoryItem = GenericFactoryItem<ReflectionDataFactory>;
GenericFactory<ReflectionDataFactory> reflectionFactory;
for (ReflectionDataFactory f : reflectionFactory)
{
const ReflectionDataInterface* i = f();
if (i) {
printf("Type: %i Name: %s Size: %i\n", i->GetTypeId(), i->GetType().c_str(), i->GetSize());
} else {
printf("Invalid reflection factory\n");
}
}
*/
/*
class EnumeratorInterface
{
public:
virtual void MoveNext();
EnumeratorInterface& operator++() { MoveNext(); return *this; }
You must overload pre-++, ensure the initialization expressions are valid,
binary != that can be used in a boolean context, unary * that returns something
you can assign-initialize range_declaration with, and expose a public destructor.
};
*/
/*
// TODO: return shared_ptr instead of raw pointer
typedef BaseGameState* (*GameStateFactory)();
using GameStateFactoryItem = GenericFactoryItem<GameStateFactory>;
template <typename T>
struct RegisterDemoContext
{
RegisterDemoContext(const char* demoName) : registrationItem(demoName, createDemo) { }
static DemoContext* createDemo() { return new T; }
DemoContextFactoryItem registrationItem;
};
// This can go in the header or cpp file, doesn't matter
#define REGISTER_DEMO_CONTEXT(demoName, demoClass) \
RegisterDemoContext<demoClass> register##demoClass(demoName);
// For debugging
void ListGameStates();
for( range_declaration : range_expression )
becomes:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
where the variables beginning with __ are for exposition only, and begin_expr and end_expr is the magic that calls begin/end.²
The requirements on the begin/end return value are simple: You must overload pre-++, ensure the initialization expressions are valid, binary != that can be used in a boolean context, unary * that returns something you can assign-initialize range_declaration with, and expose a public destructor.
void ListGameStates()
{
const GameStateFactoryItem* item = GameStateFactoryItem::getFactoryHead();
while (item)
{
Log(LL_Debug, item->m_name);
item = item->m_next;
}
}
*/
END_NAMESPACE
// So we can use HashMap together with String, we need to provide std::hash for String
namespace std
{
template <>
struct hash<NAMESPACE::String>
{
size_t operator()(const NAMESPACE::String& str) const noexcept
{
hash<std::string> h;
return h(str);
}
};
}