nkr
A C++20 library with a custom meta-programming language.
Namespaces | Classes | Concepts | Typedefs | Functions
nkr Namespace Reference

The entire library is contained within this namespace. More...

Namespaces

namespace  allocator
 Allocator types are used by other types to allocate and deallocate various kinds of memory.
 
namespace  array
 Array types are used to represent multiple values of the same type in various ways and locations.
 
namespace  boolean
 Boolean types are used to represent one of two values, and never any more than two.
 
namespace  charcoder
 Charcoder types are used to represent and work with specific character encodings.
 
namespace  concurrency
 Concurrency types are used to make other types work in a concurrent or parallel context.
 
namespace  constant
 Constant types are used to represent values that cannot be changed.
 
namespace  constant_t$
 The private namespace of nkr::constant_t.
 
namespace  cpp
 CPP types are standard C++ types that can be used like other nkr types.
 
namespace  enumeration
 Enumeration types provide abstractions over the basic C and C++ enums, making them fully usable types.
 
namespace  error
 Error types provide an abstraction over enumerations representing runtime recoverable errors.
 
namespace  interface
 Interfaces provide a small abstraction over different entities so that they can be utilized in the same way.
 
namespace  negatable
 Negatable types are used to represent numbers that can be negated, i.e. set to a negative number.
 
namespace  none
 None types are used to represent the concept of nothing.
 
namespace  os
 OS contains functionality supplied through a thin abstraction over the underlying operating system.
 
namespace  positive
 Positive types are used to represent numbers that are only ever positive and cannot be negated.
 

Classes

class  constant_t
 Represents an immutable literal value in a compile-time or run-time context. More...
 
class  constant_tg
 The identity type tag for nkr::constant_t. More...
 
class  constant_ttg
 The identity template tag for nkr::constant_t. More...
 

Concepts

concept  constant_tr
 The identity type trait for nkr::constant_t.
 
concept  constant_ttr
 The identity template trait for nkr::constant_t.
 
concept  constant_of_tr
 The identity inner type trait for nkr::constant_t.
 
concept  tr
 Used to filter a type by its qualifications, and by other types, templates, identities, and generics in the context of a declaration.
 

Typedefs

template<typename type_p >
using t = nkr::tr$::ts< AND_tg, nkr::tuple::types_t< type_p > >
 A way to wrap a single type for use with an nkr::TR expression, to differentiate it from a template. More...
 
template<nkr::generic::tag::logic_gate_tr operator_p, typename ... types_p>
using ts = nkr::tr$::ts< operator_p, nkr::tuple::types_t< types_p... > >
 A way to parenthesize and perform logical operations on several types in an nkr::TR expression. More...
 
template<template< typename ... > typename template_p>
using tt = nkr::tr$::tts< AND_tg, nkr::tuple::templates_t< template_p > >
 A way to wrap a single template for use with an nkr::TR expression, to differentiate it from a type. More...
 
template<nkr::generic::tag::logic_gate_tr operator_p, template< typename ... > typename ... templates_p>
using tts = nkr::tr$::tts< operator_p, nkr::tuple::templates_t< templates_p... > >
 A way to parenthesize and perform logical operations on several templates in an nkr::TR expression. More...
 

Functions

template<nkr::ts_tr subjects_p, typename ... expression_parts_p>
constexpr nkr::boolean::cpp_t TR () noexcept
 Used to filter a type by its qualifications, and by other types, templates, identities, and generics. More...
 

Detailed Description

The entire library is contained within this namespace.

The three letters nkr make up my initials. Inspiried by Sean T. Barrett's stb library, I named this library after myself for two reasons:

  1. The initials should prevent most name collisions when you include any of this library's headers.
  2. I have staked my name and thus reputation on the line with this library. I intend for it to be useful in the decades to come.

Typedef Documentation

◆ t

template<typename type_p >
using nkr::t = typedef nkr::tr$::ts<AND_tg, nkr::tuple::types_t<type_p> >

A way to wrap a single type for use with an nkr::TR expression, to differentiate it from a template.

◆ ts

template<nkr::generic::tag::logic_gate_tr operator_p, typename ... types_p>
using nkr::ts = typedef nkr::tr$::ts<operator_p, nkr::tuple::types_t<types_p...> >

A way to parenthesize and perform logical operations on several types in an nkr::TR expression.

◆ tt

template<template< typename ... > typename template_p>
using nkr::tt = typedef nkr::tr$::tts<AND_tg, nkr::tuple::templates_t<template_p> >

A way to wrap a single template for use with an nkr::TR expression, to differentiate it from a type.

◆ tts

template<nkr::generic::tag::logic_gate_tr operator_p, template< typename ... > typename ... templates_p>
using nkr::tts = typedef nkr::tr$::tts<operator_p, nkr::tuple::templates_t<templates_p...> >

A way to parenthesize and perform logical operations on several templates in an nkr::TR expression.

Function Documentation

◆ TR()

template<nkr::ts_tr subjects_p, typename ... expression_parts_p>
constexpr nkr::boolean::cpp_t nkr::TR ( )
constexprnoexcept

Used to filter a type by its qualifications, and by other types, templates, identities, and generics.

An integral function for the entire library. Any type or template that satisfies the nkr::interface::type_i or nkr::interface::template_i respectively can be used in conjunction with pre-defined operators to form an expression for use with this function. Input a group of subjects in addition to your arbitrary-length expression, and a compile-time or runtime boolean will be output indicating whether or not the subjects satisfy the expression.

Purpose
The primary rationale for the existence of this function is to prevent the need of defining and redefining the same concepts over and over again with only minor variations. It keeps the focus on your static tests, and more importantly when using nkr::tr, it keeps the focus on the signature of your types and functions, and what kind of types are acceptable inputs for their parameters.
Grammatical Parts
A full nkr::TR expression is made of two component parts, the subject, and an arbitrary number of operators paired with an operand:
using namespace nkr;
static_assert(TR<
/* subject */
/* operator, operand */
any_tg, t<int>
>() == true);
The entire library is contained within this namespace.
Definition: array/cpp_t_dec.h:17
constexpr nkr::boolean::cpp_t TR() noexcept
Used to filter a type by its qualifications, and by other types, templates, identities,...
nkr::tr$::ts< AND_tg, nkr::tuple::types_t< type_p > > t
A way to wrap a single type for use with an nkr::TR expression, to differentiate it from a template.
Definition: tr_dec.h:176
In the above example we statically asserted that int is any int. This time without comments:
using namespace nkr;
static_assert(TR<
any_tg, t<int>
>() == true);
Type Wrappers
A single type is wrapped with an nkr::t, or type. This syntactical addition to a nkr::TR expression allows for two powerful distinctions to occur, both of which we will learn in more detail below.
Implicit Operands
It is possible to use just a subject and an operator without using an operand. This trivial example will always return true because any subject will always be any subject:
using namespace nkr;
static_assert(TR<
any_tg
>() == true);
static_assert(TR<
any_tg
>() == true);
Any Qualification Operators
The above example may be trivial, but implicit operands become critical for operators besides any_tg. How else would we constrain a subject to a qualification regardless of type?:
using namespace nkr;
static_assert(TR<
any_const_tg
>() == false);
static_assert(TR<
any_const_tg
>() == true);
static_assert(TR<
any_const_tg
>() == false);
static_assert(TR<
any_const_tg
>() == true);
static_assert(TR<
any_volatile_tg
>() == false);
static_assert(TR<
any_volatile_tg
>() == true);
static_assert(TR<
any_volatile_tg
>() == false);
static_assert(TR<
any_volatile_tg
>() == true);
And by including an operand, you can constrain a subject to qualification as well as type:
using namespace nkr;
static_assert(TR<
any_const_tg, t<int>
>() == false);
static_assert(TR<
any_const_tg, t<int>
>() == true);
static_assert(TR<
any_const_tg, t<float>
>() == false);
static_assert(TR<
any_volatile_tg, t<float>
>() == false);
static_assert(TR<
any_volatile_tg, t<float>
>() == true);
static_assert(TR<
any_volatile_tg, t<int>
>() == false);
The reason why these are any operators is that any_const_tg will work with a volatile type as long as it's also const, and the any_volatile_tg with a const type, as long as it's also volatile:
using namespace nkr;
static_assert(TR<
any_const_tg, t<int>
>() == true);
static_assert(TR<
any_const_tg, t<int>
>() == true);
static_assert(TR<
any_volatile_tg, t<float>
>() == true);
static_assert(TR<
any_volatile_tg, t<float>
>() == true);
Likewise, any_qualified_tg merely requires that a type have any qualification:
using namespace nkr;
static_assert(TR<
any_qualified_tg, t<int>
>() == false);
static_assert(TR<
any_qualified_tg, t<int>
>() == true);
static_assert(TR<
any_qualified_tg, t<int>
>() == true);
static_assert(TR<
any_qualified_tg, t<int>
>() == true);
And inversely, the any_non_qualified_tg will only allow types without qualification:
using namespace nkr;
static_assert(TR<
any_non_qualified_tg, t<int>
>() == true);
static_assert(TR<
any_non_qualified_tg, t<int>
>() == false);
static_assert(TR<
any_non_qualified_tg, t<int>
>() == false);
static_assert(TR<
any_non_qualified_tg, t<int>
>() == false);
Yet more variants exist, such as any_non_const_tg and any_non_volatile_tg to help you explictly declare not only how to constrain the subjects of your nkr::TR expressions, but quite literally to declare the intent of your constraint in reasonably plain English.
Just Qualification Operators
It is sometimes convenient to constrain to the exact qualification of an operand. The just_tg does just that:
using namespace nkr;
static_assert(TR<
just_tg, t<const int>
>() == false);
static_assert(TR<
just_tg, t<const int>
>() == true);
static_assert(TR<
>() == false);
static_assert(TR<
>() == true);
There are several variants of the just_tg that allow for exactitude through use of the operator, as opposed to the operand:
using namespace nkr;
static_assert(TR<
just_non_qualified_tg, t<int>
>() == false);
static_assert(TR<
just_non_qualified_tg, t<int>
>() == true);
static_assert(TR<
just_const_tg, t<float>
>() == false);
static_assert(TR<
just_const_tg, t<float>
>() == true);
static_assert(TR<
just_volatile_tg, t<int>
>() == false);
static_assert(TR<
just_volatile_tg, t<int>
>() == true);
static_assert(TR<
just_const_volatile_tg, t<float>
>() == false);
static_assert(TR<
just_const_volatile_tg, t<float>
>() == false);
static_assert(TR<
just_const_volatile_tg, t<float>
>() == false);
static_assert(TR<
just_const_volatile_tg, t<float>
>() == true);
Notice how the operand's qualification is completely respected with just_tg but entirely ignored for any of the variant operators:
using namespace nkr;
static_assert(TR<
just_tg, t<const int>
>() == true);
static_assert(TR<
just_const_tg, t<volatile int>
>() == true);
static_assert(TR<
just_const_tg, t<volatile int>
>() == false);
At first, this may seem strange, but these operators are defined this way so that it is easy to avoid any errors that could result from using an alias hiding a qualification:
using namespace nkr;
using alias_of_int_t = volatile int;
static_assert(TR<
just_const_tg, t<alias_of_int_t>
>() == false);
static_assert(TR<
just_const_tg, t<alias_of_int_t>
>() == true);
Whereas the just_tg allows for the standard combination of qualifications, even with the hidden qualification of an alias:
using namespace nkr;
using alias_of_int_t = volatile int;
static_assert(TR<
>() == true);
static_assert(TR<
>() == false);
Pointer, Array, and Reference Operands
It's actually possible to test for compound built-in C++ types too:
using namespace nkr;
static_assert(TR<
any_tg, t<int*>
>() == true);
static_assert(TR<
any_tg, t<int[256]>
>() == true);
static_assert(TR<
any_tg, t<int&>
>() == true);
static_assert(TR<
any_tg, t<int&&>
>() == true);
Notice how qualification is respected for the whole type in the following examples. We use the just_tg for the sake of simplicity:
using namespace nkr;
static_assert(TR<
just_tg, t<const int*>
>() == false);
static_assert(TR<
just_tg, t<const int*>
>() == true);
static_assert(TR<
just_tg, t<int* const>
>() == false);
static_assert(TR<
just_tg, t<int* const>
>() == true);
static_assert(TR<
>() == false);
static_assert(TR<
>() == true);
static_assert(TR<
>() == false);
static_assert(TR<
>() == true);
static_assert(TR<
just_tg, t<const int&>
>() == false);
static_assert(TR<
just_tg, t<const int&>
>() == true);
static_assert(TR<
>() == false);
static_assert(TR<
>() == true);
Template Operands
It is also possible to use templates. Here we simply constrain to an instantiated nkr::pointer::cpp_t, which will result in true because nkr::pointer::cpp_t<int> is an alias of int*:
using namespace nkr;
static_assert(TR<
>() == true);
static_assert(TR<
>() == false);
Often it is useful to separate the template from its inner type which would be int in this case. This allows for more flexible expressions that make use of more than just the any_tg operator. Here we are asserting the same as above, that int* is any nkr::pointer::cpp_t of any int:
using namespace nkr;
static_assert(TR<
of_any_tg, t<int>
>() == true);
nkr::tr$::tts< AND_tg, nkr::tuple::templates_t< template_p > > tt
A way to wrap a single template for use with an nkr::TR expression, to differentiate it from a type.
Definition: tr_dec.h:189
Tag Operands
Alternatively we can use a ttg, that is a template tag of nkr::pointer::cpp_t to achieve the same result:
using namespace nkr;
static_assert(TR<
of_any_tg, t<int>
>() == true);
The use of tags can bring even more flexibility to an expression. For example, most templates provide a tag to test if a type is instantiated from it, regardless of the subject's actual inner type. Below we simply assert that the subject needs to be instantiated from any nkr::pointer::cpp_t by use of its tg or type tag:
using namespace nkr;
static_assert(TR<
>() == true);
static_assert(TR<
>() == true);
Note the symetrical use of tt with ttg to constrain a template tag and t with tg to constrain a type tag.
Type Wrappers and Template Wrappers
Types are wrapped with nkr::t or nkr::ts, that is type or types respectively. This syntactical feature differentiates between using a singluar type or multiple types in a subject or operand. It also differentiates between using types or templates as an operand. To use templates as an operand you would use nkr::tt or nkr::tts, that is 'template type' or 'template types' respectively. It is necessary to wrap the templates this way for the sake of arbitrary length expressions, which cannot be made up of both types and templates but only one or the other.
Generic Operands
Tags become absolutely critical when we start to introduce generics into our expressions.
Arbitrary Length
Arbitrary length expressions allow for the constraint of templates of templates of templates, ..., of templates of types. Here we have an intentionally complex example:
using namespace nkr;
static_assert(TR<
of_any_tg, t<int>
>() == true);
However it is possible to find real-world examples of two or even three operands in use with an nkr::TR expression throughout the library, with various templates and types coming into play. A classic C++ example would be an lvalue reference to a pointer which one might pass to a function for initialization:
using namespace nkr;
static_assert(TR<
of_any_non_const_tg, tt<nkr::pointer::cpp_t>,
of_any_tg, t<int>
>() == true);
In this case our function would not care what the pointer is pointing to, or that its inner type is const. It only cares that it is a reference to a pointer that is non_const, else it wouldn't be able to set it. It doesn't actualy care if the pointer is volatile or not because its functionality remains the same in that event.
Target Operand
Finally, because the subject of nkr::TR must always be a type and not a template, so too must the last operand be a type and not a template, even if that type is a type tag representing a template.
Using Multiple Types as an Operand
Naturally there are times when using more than one type as a single operand in an nkr::TR expression is desirable. Sometimes one may wish to constrain to several operands parenthetically, to be evaluated by a logical operator. That's where nkr::ts comes in.
Here we constrain to either type in the operand, with use of the nkr::OR_tg:
using namespace nkr;
static_assert(TR<t<nkr::negatable::integer_t>,
static_assert(TR<t<nkr::negatable::real_t>,
nkr::tr$::ts< operator_p, nkr::tuple::types_t< types_p... > > ts
A way to parenthesize and perform logical operations on several types in an nkr::TR expression.
Definition: tr_dec.h:181
To both generics in the operand, with use of the nkr::AND_tg:
using namespace nkr;
static_assert(TR<t<nkr::negatable::integer_t>,
static_assert(TR<t<nkr::negatable::real_t>,
To just one generic in the operand, with use of the nkr::XOR_tg:
using namespace nkr;
static_assert(TR<t<nkr::boolean::cpp_t>,
static_assert(TR<t<nkr::boolean::pure_t>,
Using Multiple Types as a Subject
You can also use nkr::ts for multiple subjects. Here we've built an entire set of truth tables where each subject is tested against the singular operand. Notice how the complement for each of the above operators is also in play here, namely nkr::NOR_tg, nkr::NAND_tg, and nkr::XNOR_tg:
using namespace nkr;
using true_t = nkr::positive::integer_t;
using false_t = nkr::negatable::integer_t;
static_assert(TR<ts<OR_tg, false_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<OR_tg, false_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<OR_tg, true_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<OR_tg, true_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<AND_tg, false_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<AND_tg, false_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<AND_tg, true_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<AND_tg, true_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XOR_tg, false_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XOR_tg, false_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XOR_tg, true_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XOR_tg, true_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XOR_tg, false_t, false_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XOR_tg, true_t, false_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XOR_tg, false_t, true_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XOR_tg, false_t, false_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XOR_tg, true_t, true_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XOR_tg, false_t, true_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XOR_tg, true_t, false_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XOR_tg, true_t, true_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<NOR_tg, false_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<NOR_tg, false_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<NOR_tg, true_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<NOR_tg, true_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<NAND_tg, false_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<NAND_tg, false_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<NAND_tg, true_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<NAND_tg, true_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XNOR_tg, false_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XNOR_tg, false_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XNOR_tg, true_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XNOR_tg, true_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XNOR_tg, false_t, false_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XNOR_tg, true_t, false_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XNOR_tg, false_t, true_t, false_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XNOR_tg, false_t, false_t, true_t>, any_tg, t<true_t>>() == false);
static_assert(TR<ts<XNOR_tg, true_t, true_t, false_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XNOR_tg, false_t, true_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XNOR_tg, true_t, false_t, true_t>, any_tg, t<true_t>>() == true);
static_assert(TR<ts<XNOR_tg, true_t, true_t, true_t>, any_tg, t<true_t>>() == true);
Using Multiple Templates as an Operand
Following the pattern of postfixes, with t for type and tt for template of type, we also likewise use nkr::tts instead of nkr::ts to make use of multiple templates in an operand:
using namespace nkr;
static_assert(TR<t<nkr::positive::integer_t*>,
static_assert(TR<t<nkr::positive::integer_t[1]>,
nkr::tr$::tts< operator_p, nkr::tuple::templates_t< templates_p... > > tts
A way to parenthesize and perform logical operations on several templates in an nkr::TR expression.
Definition: tr_dec.h:194