Skip to content

Commit

Permalink
Clean up common.hpp and add unit tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Jul 16, 2018
1 parent e5a3dba commit d7089e4
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 259 deletions.
17 changes: 17 additions & 0 deletions compilation_tests/assign_noncopyable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <alia/common.hpp>

using namespace alia;

struct foo : noncopyable
{
};

void
f()
{
foo x;
foo y;
#ifdef ALIA_TEST_COMPILATION_FAILURE
y = x;
#endif
}
16 changes: 16 additions & 0 deletions compilation_tests/copy_construct_noncopyable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <alia/common.hpp>

using namespace alia;

struct foo : noncopyable
{
};

void
f()
{
foo x;
#ifdef ALIA_TEST_COMPILATION_FAILURE
foo y(x);
#endif
}
257 changes: 7 additions & 250 deletions src/alia/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,157 +17,6 @@ namespace alia {

typedef long long counter_type;

using std::int16_t;
using std::int8_t;
using std::uint16_t;
using std::uint8_t;

typedef std::string string;

typedef char const* utf8_ptr;

struct utf8_string
{
utf8_ptr begin, end;

utf8_string()
{
}
utf8_string(utf8_ptr begin, utf8_ptr end) : begin(begin), end(end)
{
}
};

static inline bool
is_empty(utf8_string const& text)
{
return text.begin == text.end;
}

static inline utf8_string
as_utf8_string(string const& text)
{
return utf8_string(text.c_str(), text.c_str() + text.length());
}

template<typename T>
T
clamp(T x, T min, T max)
{
assert(min <= max);
return (std::min)((std::max)(x, min), max);
}

// A flag_set is a set of flags, each of which represents a boolean property.
// It is implemented as a simple unsigned integer, where each bit represents
// a different property.
// (The property codes must be defined manually as constants.)
// The advantage of using this over plain unsigned integers is that this is
// type-safe and slightly more explicit.
// A flag_set has a Tag type that identifies the set of properties that go with
// it. Only properties/sets with the same tag can be combined.

// NO_FLAGS can be implicitly converted to any type of flag_set.
struct null_flag_set
{
};
static null_flag_set const NO_FLAGS = null_flag_set();

template<class Tag>
struct flag_set
{
unsigned code;
flag_set()
{
}
flag_set(null_flag_set) : code(0)
{
}
explicit flag_set(unsigned code) : code(code)
{
}
// allows use within if statements without other unintended conversions
typedef unsigned flag_set::*unspecified_bool_type;
operator unspecified_bool_type() const
{
return code != 0 ? &flag_set::code : 0;
}
};

template<class Tag>
flag_set<Tag>
operator|(flag_set<Tag> a, flag_set<Tag> b)
{
return flag_set<Tag>(a.code | b.code);
}
template<class Tag>
flag_set<Tag>&
operator|=(flag_set<Tag>& a, flag_set<Tag> b)
{
a.code |= b.code;
return a;
}
template<class Tag>
flag_set<Tag> operator&(flag_set<Tag> a, flag_set<Tag> b)
{
return flag_set<Tag>(a.code & b.code);
}
template<class Tag>
flag_set<Tag>&
operator&=(flag_set<Tag>& a, flag_set<Tag> b)
{
a.code &= b.code;
return a;
}
template<class Tag>
bool
operator==(flag_set<Tag> a, flag_set<Tag> b)
{
return a.code == b.code;
}
template<class Tag>
bool
operator!=(flag_set<Tag> a, flag_set<Tag> b)
{
return a.code != b.code;
}
template<class Tag>
bool
operator<(flag_set<Tag> a, flag_set<Tag> b)
{
return a.code < b.code;
}
template<class Tag>
flag_set<Tag>
operator~(flag_set<Tag> a)
{
return flag_set<Tag>(~a.code);
}
} // namespace alia

namespace std {
template<class Tag>
struct hash<alia::flag_set<Tag>>
{
size_t
operator()(alia::flag_set<Tag> const& set) const
{
return hash<unsigned>()(set.code);
}
};
} // namespace std

namespace alia {
#define ALIA_DEFINE_FLAG_TYPE(type_prefix) \
struct type_prefix##_flag_tag \
{ \
}; \
typedef alia::flag_set<type_prefix##_flag_tag> type_prefix##_flag_set;

#define ALIA_DEFINE_FLAG(type_prefix, code, name) \
static unsigned const name##_CODE = code; \
static alia::flag_set<type_prefix##_flag_tag> const name(code);

// Inspired by Boost, inheriting from noncopyable disables copying for a type.
// The namespace prevents unintended ADL if used by applications.
namespace impl {
Expand All @@ -188,9 +37,10 @@ struct noncopyable
} // namespace impl
typedef impl::noncopyable_::noncopyable noncopyable;

// general-purpose exception class for alia
struct exception : std::exception
{
exception(string const& msg) : msg_(new string(msg))
exception(std::string const& msg) : msg_(new std::string(msg))
{
std::cout << msg << std::endl;
}
Expand All @@ -207,109 +57,14 @@ struct exception : std::exception

// Add another level of context to the error messsage.
void
add_context(string const& str)
add_context(std::string const& str)
{
*msg_ += "\n" + str;
}

private:
std::shared_ptr<string> msg_;
};

// optional<T> stores an optional value of type T (or no value).
struct none_type
{
none_type()
{
}
};
static none_type const none;
template<class T>
struct optional
{
typedef T value_type;
optional() : valid_(false)
{
}
optional(T const& value) : value_(value), valid_(true)
{
}
optional(none_type) : valid_(false)
{
}
optional&
operator=(T const& value)
{
value_ = value;
valid_ = true;
return *this;
}
optional& operator=(none_type)
{
valid_ = false;
return *this;
}
// allows use within if statements without other unintended conversions
typedef bool optional::*unspecified_bool_type;
operator unspecified_bool_type() const
{
return valid_ ? &optional::valid_ : 0;
}
T const&
get() const
{
assert(valid_);
return value_;
}
T&
get()
{
assert(valid_);
return value_;
}

private:
T value_;
bool valid_;
std::shared_ptr<std::string> msg_;
};
template<class T>
T const&
get(optional<T> const& opt)
{
return opt.get();
}
template<class T>
T&
get(optional<T>& opt)
{
return opt.get();
}

template<class T>
bool
operator==(optional<T> const& a, optional<T> const& b)
{
return a ? (b && get(a) == get(b)) : !b;
}
template<class T>
bool
operator!=(optional<T> const& a, optional<T> const& b)
{
return !(a == b);
}
template<class T>
bool
operator<(optional<T> const& a, optional<T> const& b)
{
return b && (a ? get(a) < get(b) : true);
}

template<class T>
optional<T>
some(T const& x)
{
return optional<T>(x);
}

// Invoke the standard hash function for a value.
template<class T>
Expand All @@ -320,7 +75,8 @@ invoke_hash(T const& x)
}

// Combine two hash values.
size_t static inline combine_hashes(size_t a, size_t b)
static inline size_t
combine_hashes(size_t a, size_t b)
{
return a ^ (0x9e3779b9 + (a << 6) + (a >> 2) + b);
}
Expand All @@ -331,6 +87,7 @@ size_t static inline combine_hashes(size_t a, size_t b)
#else
#define ALIA_UNUSED
#endif

} // namespace alia

#endif
8 changes: 4 additions & 4 deletions src/alia/signals/basic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ direct(Value& x)

// text(x), where x is a string constant, creates a read-only signal for
// accessing x as a string.
struct text : signal<string, read_only_signal>
struct text : signal<std::string, read_only_signal>
{
text(char const* x) : text_(x)
{
Expand All @@ -144,15 +144,15 @@ struct text : signal<string, read_only_signal>
{
return true;
}
string const&
std::string const&
read() const
{
return lazy_reader_.read([&] { return string(text_); });
return lazy_reader_.read([&] { return std::string(text_); });
}

private:
char const* text_;
lazy_reader<string> lazy_reader_;
lazy_reader<std::string> lazy_reader_;
};

} // namespace alia
Expand Down
33 changes: 33 additions & 0 deletions unit_tests/common.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <alia/common.hpp>

#include <catch.hpp>

using namespace alia;

TEST_CASE("exception", "[common]")
{
exception e("just a test");
REQUIRE(e.what() == std::string("just a test"));
e.add_context("in here");
REQUIRE(e.what() == std::string("just a test\nin here"));
}

TEST_CASE("invoke_hash", "[common]")
{
REQUIRE(invoke_hash(17) == std::hash<int>()(17));
}

TEST_CASE("combine_hashes", "[common]")
{
// It's hard to establish specific requirements for this, but these seem
// reasonable as basic requirements.

// Argument order matters.
REQUIRE(combine_hashes(17, 12) != combine_hashes(12, 17));

// The first argument matters.
REQUIRE(combine_hashes(17, 12) != combine_hashes(16, 12));

// The second argument matters.
REQUIRE(combine_hashes(17, 12) != combine_hashes(17, 11));
}

0 comments on commit d7089e4

Please sign in to comment.