Skip to content

Commit

Permalink
Get rid of dynamic context checks.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Sep 29, 2020
1 parent 2000a9f commit 77ef676
Show file tree
Hide file tree
Showing 5 changed files with 6 additions and 304 deletions.
14 changes: 0 additions & 14 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,4 @@ use this option.

</dd>

<dt>ALIA_DYNAMIC_CONTEXT_CHECKS</dt><dd>

This switches to using dynamic context checking. (This is somewhat experimental,
but more so in terms of whether or not it's useful rather than whether or not it
works correctly.)

Normally, when you try to call a function that requires a tag/object that you're
not providing in your context, you'll get a compile-time error. This changes
that to a run-time error. This eliminates a lot of template shenanigans inside
alia (at the expense of a few more run-time checks), so it might improve your
compilation times during development. It's not recommended for production.

</dd>

</dl>
190 changes: 6 additions & 184 deletions src/alia/context/typing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@
// that component (but ubiquitous within it).
//
// 2. Functions that take contexts as arguments should be able to define the
// set
// of context elements that they require as part of the type signature of
// the context. (Context elements would be identified by compile-time type
// tags.) Any caller whose context includes a superset of those tags should
// be able to call the function with an implicit conversion of the context
// parameter. This should all be possible without needing to define
// set of context elements that they require as part of the type signature
// of the context. (Context elements would be identified by compile-time
// type tags.) Any caller whose context includes a superset of those tags
// should be able to call the function with an implicit conversion of the
// context parameter. This should all be possible without needing to define
// functions as templates (otherwise alia-based applications would end up
// being entirely header-based) and with minimal (ideally zero) runtime
// overhead in converting the caller's context to the type expected by the
Expand All @@ -39,18 +38,6 @@
// 3. Retrieving frames/capabilities from a context should require minimal
// (ideally zero) runtime overhead.
//
// 4. Static type checking of context conversions/retrievals should be possible
// but optional. Developers should not have to pay these compile-time costs
// unless it's desired. It should be possible to use a mixed workflow where
// these checks are replaced by runtime checks for iterative development but
// enabled for CI/release builds.
//
// In order to satisfy #4, this file looks for a #define called
// ALIA_DYNAMIC_CONTEXT_CHECKS. If this is set, code related to statically
// checking context contents is omitted and dynamic checks are substituted
// where appropriate. Note that when ALIA_DYNAMIC_CONTEXT_CHECKS is NOT set,
// ALIA_STATIC_CONTEXT_CHECKS is set and static checks are included.
//
// The statically typed structural_collection object is a simple wrapper around
// the dynamically typed storage object. It adds a compile-time type list
// indicating what's actually supposed to be in the collection. This allows
Expand All @@ -62,10 +49,6 @@
// passing by reference would be more obvious, but that seems unavoidable given
// the requirements.

#ifndef ALIA_DYNAMIC_CONTEXT_CHECKS
#define ALIA_STATIC_CONTEXT_CHECKS
#endif

namespace alia {

namespace impl {
Expand All @@ -79,8 +62,6 @@ namespace impl {
template<class Tags, class Storage>
struct structural_collection;

#ifdef ALIA_STATIC_CONTEXT_CHECKS

// tag_list<Tags...> defines a simple compile-time list of tags. This is held
// by a structural_collection to provide compile-time tracking of its contents.
template<class... Tags>
Expand Down Expand Up @@ -364,103 +345,6 @@ template<class A, class B>
using merge_structural_collections_t =
typename merge_structural_collections<A, B>::type;

#else

struct dynamic_tag_list
{
};

template<class Tags, class Storage>
struct structural_collection
{
typedef Tags tags;
typedef Storage storage_type;

structural_collection(Storage* storage) : storage(storage)
{
}

Storage* storage;
};

// structural_collection_is_convertible<From,To>::value yields a
// compile-time boolean indicating whether or not the type :From can be
// converted to the type :To (both must be structural_collections).
// Since this is the dynamic version, the requirements for this are simply that
// the storage types are the same.
template<class From, class To>
struct structural_collection_is_convertible
{
};
// case where storage types differ
template<class FromTags, class FromStorage, class ToTags, class ToStorage>
struct structural_collection_is_convertible<
structural_collection<FromTags, FromStorage>,
structural_collection<ToTags, ToStorage>> : std::false_type
{
};
// case where storage types are the same
template<class Storage, class FromTags, class ToTags>
struct structural_collection_is_convertible<
structural_collection<FromTags, Storage>,
structural_collection<ToTags, Storage>> : std::true_type
{
};

// empty_structural_collection<Storage> yields a structural collection with no
// item and :Storage as its storage type.
template<class Storage>
using empty_structural_collection
= structural_collection<dynamic_tag_list, Storage>;

// add_tagged_data_type<Collection,Tag>::type gives the type that results from
// extending :Collection with the data type defined by :Tag and :Data.
template<class Collection, class Tag>
struct add_tagged_data_type
{
typedef Collection type;
};
template<class Collection, class Tag>
using add_tagged_data_type_t =
typename add_tagged_data_type<Collection, Tag>::type;

// add_tagged_data_types<Collection,Tag...>::type gives the type that results
// from extending :Collection with the data types defined by the given list of
// tags.
template<class Collection, class... Tag>
struct add_tagged_data_types
{
typedef Collection type;
};
template<class Collection, class... Tag>
using add_tagged_data_types_t =
typename add_tagged_data_types<Collection, Tag...>::type;

// remove_tagged_data_type<Collection,Tag>::type yields the type that results
// from removing the data type associated with :Tag from :Collection.
template<class Collection, class Tag>
struct remove_tagged_data_type
{
typedef Collection type;
};
template<class Collection, class Tag>
using remove_tagged_data_type_t =
typename remove_tagged_data_type<Collection, Tag>::type;

// merge_structural_collections<A,B>::type yields a structural collection type
// that contains all the tags from :A and :B (but no duplicates). Note that the
// resulting tags collection inherits the storage type of :A.
template<class A, class B>
struct merge_structural_collections
{
typedef A type;
};
template<class A, class B>
using merge_structural_collections_t =
typename merge_structural_collections<A, B>::type;

#endif

// Make an empty structural collection for the given storage object.
template<class Storage>
empty_structural_collection<Storage>
Expand Down Expand Up @@ -498,73 +382,16 @@ add_tagged_data(Collection collection, Data&& data)
template<class Tag, class Collection>
remove_tagged_data_type_t<Collection, Tag>
remove_tagged_data(Collection collection)
{
typename Collection::storage_type* storage = collection.storage;
// We only actually have to remove the item if we're using dynamic context
// checking. With static checking, it doesn't matter if the runtime storage
// includes an extra item. Static checks will prevent its use.
#ifdef ALIA_DYNAMIC_CONTEXT_CHECKS
// Remove the item from the storage object.
storage->template remove<Tag>();
#endif
return remove_tagged_data_type_t<Collection, Tag>(storage);
}

// Remove an item from a collection.
//
// With this version, you supply a new storage object, and the function uses it
// if needed to ensure that the original collection's storage is left
// untouched.
//
#ifdef ALIA_STATIC_CONTEXT_CHECKS
template<class Tag, class Collection, class Storage>
remove_tagged_data_type_t<Collection, Tag>
remove_tagged_data(Collection collection, Storage*)
{
return remove_tagged_data_type_t<Collection, Tag>(collection.storage);
}
#else
template<class Tag, class Collection, class Storage>
remove_tagged_data_type_t<Collection, Tag>
remove_tagged_data(Collection collection, Storage* new_storage)
{
*new_storage = *collection.storage;
new_storage->template remove<Tag>();
return remove_tagged_data_type_t<Collection, Tag>(new_storage);
}
#endif

// Determine if a tag is in a collection.
#ifdef ALIA_STATIC_CONTEXT_CHECKS
template<class Tag, class Collection>
bool has_tagged_data(Collection)
{
return structural_collection_contains_tag<Collection, Tag>::value;
}
#else
template<class Tag, class Collection>
bool
has_tagged_data(Collection collection)
{
return collection.storage->template has<Tag>();
}
#endif

#ifdef ALIA_DYNAMIC_CONTEXT_CHECKS

// When using dynamic context checking, this error is thrown when trying to
// retrieve a tag that's not actually present in a collection.
template<class Tag>
struct tagged_data_not_found : exception
{
tagged_data_not_found()
: exception(
std::string("tag not found in context:\n") + typeid(Tag).name())
{
}
};

#endif

// tagged_data_caster should be specialized so that it properly casts from
// stored data to the expected types.
Expand All @@ -574,7 +401,7 @@ struct tagged_data_caster
{
};

// If we're stored what's expected, then the cast is trivial.
// If we've stored what's expected, then the cast is trivial.
template<class T>
struct tagged_data_caster<T, T>
{
Expand All @@ -601,14 +428,9 @@ template<class Tag, class Collection>
decltype(auto)
get_tagged_data(Collection collection)
{
#ifdef ALIA_STATIC_CONTEXT_CHECKS
static_assert(
structural_collection_contains_tag<Collection, Tag>::value,
"tag not found in context");
#else
if (!has_tagged_data<Tag>(collection))
throw tagged_data_not_found<Tag>();
#endif
return tagged_data_caster<
decltype(collection.storage->template get<Tag>()),
typename Tag::data_type>::apply(collection.storage
Expand Down
11 changes: 0 additions & 11 deletions src/alia/flow/macros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,23 +323,12 @@ read_condition(T const& x)
// This means that any attempt to retrieve data within the block will result
// in an error (as it should).

#ifdef ALIA_STATIC_CONTEXT_CHECKS
#define ALIA_REMOVE_DATA_TRACKING(ctx) \
ALIA_DISABLE_MACRO_WARNINGS \
auto _alia_ctx = alia::make_context( \
alia::impl::remove_tagged_data<data_traversal_tag>(ctx.contents_)); \
auto ctx = _alia_ctx; \
ALIA_UNDISABLE_MACRO_WARNINGS
#else
#define ALIA_REMOVE_DATA_TRACKING(ctx) \
ALIA_DISABLE_MACRO_WARNINGS \
typename decltype(ctx)::contents_type::storage_type _alia_storage; \
auto _alia_ctx = alia::make_context( \
alia::impl::remove_tagged_data<data_traversal_tag>( \
ctx.contents_, &_alia_storage)); \
auto ctx = _alia_ctx; \
ALIA_UNDISABLE_MACRO_WARNINGS
#endif

#define ALIA_UNTRACKED_IF_(ctx, condition) \
if (alia::condition_is_true(condition)) \
Expand Down
90 changes: 0 additions & 90 deletions unit_tests/context/dynamic_typing.cpp

This file was deleted.

0 comments on commit 77ef676

Please sign in to comment.