Simple constexpr LookUpTable in C++14 - c++

I am trying to make a simple LookUpTable based on an array of integers, where the idea is to have it calculated at compile time.
Trying to make it possible to use it for any other future tables of various integer types I might have, I need it as a template.
So I have a LookUpTable.h
#ifndef LOOKUPTABLE_H
#define LOOKUPTABLE_H
#include <stdexcept> // out_of_range
template <typename T, std::size_t NUMBER_OF_ELEMENTS>
class LookUpTableIndexed
{
private:
//constexpr static std::size_t NUMBER_OF_ELEMENTS = N;
// LookUpTable
T m_lut[ NUMBER_OF_ELEMENTS ] {}; // ESSENTIAL T Default Constructor for COMPILE-TIME INTERPRETER!
public:
// Construct and Populate the LookUpTable such that;
// INDICES of values are MAPPED to the DATA values stored
constexpr LookUpTableIndexed() : m_lut {}
{
//ctor
}
// Returns the number of values stored
constexpr std::size_t size() const {return NUMBER_OF_ELEMENTS;}
// Returns the DATA value at the given INDEX
constexpr T& operator[](std::size_t n)
{
if (n < NUMBER_OF_ELEMENTS)
return m_lut[n];
else throw std::out_of_range("LookUpTableIndexed[] : OutOfRange!");
}
constexpr const T& operator[](std::size_t n) const
{
if (n < NUMBER_OF_ELEMENTS)
return m_lut[n];
else throw std::out_of_range("LookUpTableIndexed[] const : OutOfRange!");
}
using iterator = T*;
// Returns beginning and end of LookUpTable
constexpr iterator begin() {return &m_lut[0 ];}
constexpr iterator end () {return &m_lut[NUMBER_OF_ELEMENTS];}
};
#endif // LOOKUPTABLE_H
And I'm trying to use it in a class for rapid attenuation of an integer signal wrt an integer distance.
eg. This is just a sample usage as Foo.h
#ifndef FOO_H
#define FOO_H
#include <limits> // max, digits
#include <stdlib.h> // abs
#include "LookUpTable.h" // LookUpTableIndexed
class Foo
{
private:
template <typename TDistance,
TDistance MAXIMUM_DISTANCE,
std::size_t NUMBER_OF_DIGITS>
struct DistanceAttenuation
{
private:
// Maximum value that can be held in this type
//constexpr auto MAXIMUM_DISTANCE = std::numeric_limits<TDistance>::max();
// Number of bits used by this type
//constexpr auto NUMBER_OF_DIGITS = std::numeric_limits<TDistance>::digits;
// LookUpTable
LookUpTableIndexed<TDistance, NUMBER_OF_DIGITS> m_attenuationRangeUpperLimit {}; // ESSENTIAL LookUpTable Default Constructor for COMPILE-TIME INTERPRETER!
// Returns the number of bits to BIT-SHIFT-RIGHT, attenuate, some signal
// given its distance from source
constexpr std::size_t attenuateBy(const TDistance distance)
{
for (std::size_t i {NUMBER_OF_DIGITS}; (i > 0); --i)
{
// While distance exceeds upper-limit, keep trying values
if (distance >= m_attenuationRangeUpperLimit[i - 1])
{
// Found RANGE the given distance occupies
return (i - 1);
}
}
throw std::logic_error("DistanceAttenuation::attenuateBy(Cannot attenuate signal using given distance!)");
}
public:
// Calculate the distance correction factors for signals
// so they can be attenuated to emulate the the effects of distance on signal strength
// ...USING THE INVERSE SQUARE RELATIONSHIP OF DISTANCE TO SIGNAL STRENGTH
constexpr DistanceAttenuation() : m_attenuationRangeUpperLimit {}
{
//ctor
// Populate the LookUpTable
for (std::size_t i {0}; (i < NUMBER_OF_DIGITS); ++i)
{
TDistance goo = 0; // Not an attenuation calculation
TDistance hoo = 0; // **FOR TEST ONLY!**
m_attenuationRangeUpperLimit[i] = MAXIMUM_DISTANCE - goo - hoo;
}
static_assert((m_attenuationRangeUpperLimit[0] == MAXIMUM_DISTANCE),
"DistanceAttenuation : Failed to Build LUT!");
}
// Attenuate the signal, s, by the effect of the distance
// by some factor, a, where;
// Positive contribution values are attenuated DOWN toward ZERO
// Negative UP ZERO
constexpr signed int attenuateSignal(const signed int s, const int a)
{
return (s < 0)? -(abs(s) >> a) :
(abs(s) >> a);
}
constexpr signed int attenuateSignalByDistance(const signed int s, const TDistance d)
{
return attenuateSignal(s, attenuateBy(d));
}
};
using SDistance_t = unsigned int;
constexpr static auto m_distanceAttenuation = DistanceAttenuation<SDistance_t,
std::numeric_limits<SDistance_t>::max(),
std::numeric_limits<SDistance_t>::digits>();
public:
Foo() {}
~Foo() {}
// Do some integer foo
signed int attenuateFoo(signed int signal, SDistance_t distance) {return m_distanceAttenuation::attenuateSignalByDistance(signal, distance);}
};
#endif // FOO_H
I have tried to do this several ways, using the youtube video tutorial by CppCon 2015: Scott Schurr “constexpr: Applications" and others, but it won't compile giving the error;
error: 'constexpr static auto m_distanceAttenuation...' used before its definition
and the static asserts fail with
error: non-constant condition for static assertion
indicating it isn't calculating anything at compile-time.
I'm new to C++.
I know I'm doing something obvious but I don't know what it is.
Am I misusing static or constexpr?
numeric_limits are constexpr?
What am I doing wrong?
Thank you.

Some observations
1) as observed by michalsrb, Foo isn't complete when you initialize m_distanceAttenuation and DistanceAttenuation is part of Foo, so is incomplete.
Unfortunately you can't initialize a static constexpr member with an incomplete type (as better explained by jogojapan in this answer).
Suggestion: define DistanceAttenuation it outside (and before) Foo; so it's a complete type and can be used to initialize m_distanceAttenuation; something like
template <typename TDistance,
TDistance MAXIMUM_DISTANCE,
std::size_t NUMBER_OF_DIGITS>
struct DistanceAttenuation
{
// ...
};
class Foo
{
// ...
};
2) in C++14, a constexpr method isn't a const method; suggestion: define the following method as const too or you can't use they in some constexpr expressions
constexpr std::size_t attenuateBy (const TDistance distance) const
constexpr signed int attenuateSignal(const signed int s, const int a) const
constexpr signed int attenuateSignalByDistance(const signed int s, const TDistance d) const
3) in attenuateBy(), the test in the following for is ever true
for (std::size_t i {NUMBER_OF_DIGITS - 1}; (i >= 0); --i)
because a std::size_t is ever >= 0, so the for goes in loop and never exit; suggestion: redefine i as int or long
4) in attenuateFoo() you use m_DistanceAttenuation where the variable is defined as m_distanceAttenuation; suggestion: correct che name of the variable used
5) in attenuateFoo() you call the method attenuateSignalByDistance() using the :: operator; suggestion: use the . operator, so (considering point (4) too)
signed int attenuateFoo(signed int signal, SDistance_t distance)
{return m_distanceAttenuation.attenuateSignalByDistance(signal, distance);}

Related

C++ Fixed Range Int

I'd like to create a type with a compile-time guarantee that instances are one of N possible values. The values are int types. No arithmetic needed.
One possible solution is to use an enum class, but this scales poorly with N.
I think the solution would look most like a fixed range int. I imagine the usage would look something like this, but i'm not too sure how to implement this.Is something like this possible or am I limited to enum classes?
constexpr auto N = 60;
MyType<N> foo(30); // okay
MyType<N> foo(0); // okay
MyType<N> foo(59); // okay
MyType<N> bar(60); // compile error
MyType<N> boo(-1); // compile error
I don't think it's possible to get compile error, however you can limit it using custom class something like this:
#include <stdexcept>
class MyIntger {
public:
MyIntger(int num){
if(num < Min || num > Max){
throw std::runtime_error("Number out of range");
}
value = num;
}
private:
int value;
const static int Min = 0;
const static int Max = 60;
};
int main(){
MyIntger a(5); // OK
MyIntger b(61); // Throws std::runtime_error
}
You can have compile-time ints in C++ (and thus compile-time limit checking), but syntax is not the same as for runtime ints:
#include <type_traits>
template<size_t N>
struct MyInteger
{
// you might have it as T or limit it to size_t
template<typename T, T I>
MyInteger(std::integral_constant<T, I>)
: value(I) { static_assert(I < N); }
size_t value;
};
int main()
{
MyInteger<60> a(std::integral_constant<size_t, 59>()); // ok
MyInteger<60> b(std::integral_constant<size_t, 60>()); // error
}
If you don't like the long name, you can always have a template type alias, e.g.:
template<size_t K>
std::integral_constant<size_t, K> I = {};
int main()
{
MyInteger<60> a(I<59>);
}

C++ std::plus as template parameter

I wanna write a class for a binary indexed array,
which use two non-type default template parameter, op and identity.
And need to check the constraint that op(identity,identity) == identity.
My problem is,
I don't to how to specify op, my current solution does not compile.
‘class std::function<T(T, T)>’ is not a valid type for a template non-type parameter
how to to check op(identity,identity) == identity, currently I cannot verify, since failed on step 1, maybe static_assert?
So currently I use below workaround, but then I cannot specify op, eg, std::multiplies<int>.
Can anyone tell me how to achieve the goal?
#include <vector>
#include <functional>
// template <typename T = int, std::function<T(T,T)> op = std::plus<T>(), const T identity = T()>
template <typename T = int, const T identity = T()> // currently workaround
class BIT { // binary indexed array
const std::function<T(T,T)> op = std::plus<T>(); // currently workaround
public:
BIT(std::vector<T> value) : value(value), prefixSum(value.size() + 1, identity) {
for (size_t i = 1; i < prefixSum.size(); ++i) {
incrementNodeByValue(i, value[i-1]);
}
// print(prefixSum,"prefixSum");
}
T getSum(size_t i) {
auto sum = identity;
while (i) {
sum = op(sum, prefixSum(i));
i = firstSmallerAncestor(i);
}
return sum;
}
void incrementNodeByValue(size_t i, T x) {
while (i < prefixSum.size()) {
prefixSum[i] = op(prefixSum[i], x);
i = firstLargerAncestor(i);
}
}
private:
inline size_t firstLargerAncestor(size_t node) { return node + (node & -node); }
inline size_t firstSmallerAncestor(size_t node) { return node & (node - 1); }
std::vector<T> value;
std::vector<T> prefixSum;
};
int main() {
auto vec = std::vector<int> {5,1,15,11,52,28,0};
auto bit = BIT<>(vec);
}
The use of std::function here is a waste and seems to be the source of your confusion.
Note that templates may only be parameterized on typenames and values of integral types (char, int, long, etc). Here you're attempting to parameterize on a value of a std::function instantiation, which isn't an integral type. That said, you don't actually need to parameterize on a value in this case.
Because your constructor doesn't accept an argument to initialize the op member variable nor is it accessible via the interface, I gather it's safe to assume the operator is known at compile-time, is guaranteed immutable, and default constructible.
As such, I declared the op member to be of a parameter type called operation.
#include <functional>
#include <vector>
template< typename T = int,
typename operation = std::plus<T>,
const T identity = T() >
class BIT {
const operation op = operation();
static_assert( operation()(identity, identity) == identity );
std::vector<T> value;
std::vector<T> prefixSum;
inline size_t firstLargerAncestor(size_t node) { return node + (node & -node); }
inline size_t firstSmallerAncestor(size_t node) { return node & (node - 1); }
public:
BIT(std::vector<T> value) :
value(value),
prefixSum(value.size() + 1, identity) {
for (size_t i = 1; i < prefixSum.size(); ++i) {
incrementNodeByValue(i, value[i-1]);
}
}
T getSum(size_t i) {
auto sum = identity;
while (i) {
sum = op(sum, prefixSum(i));
i = firstSmallerAncestor(i);
}
return sum;
}
void incrementNodeByValue(size_t i, T x) {
while (i < prefixSum.size()) {
prefixSum[i] = op(prefixSum[i], x);
i = firstLargerAncestor(i);
}
}
};
live example
As a note, you'll likely want to define an identity template elsewhere to parameterized on an operation and value types to default the third parameter here. As is, it seems you'll almost always be defining all three parameters during instantiation.

c++ pointers & Arrays

I am really at my wits end with trying to get pointers to work on c++. I have searched so many questions, but I just cant understand these things.
I am trying to do the equivalent of this python code in c++ :
class Node:
def __init__(self,isBase,channelList):
self.isBase = isBase
self.channelList = channelList
def getChannelList(self):
return self.channelList
def isBase(self):
return self.isBase
Channel list is the difficult bit. It is an array of integers.
I know that this will be passed as a pointer to my class declaration. I want to be able to store this in a class variable, and be able to get and set it on command.
The C++ Code is as follows:
#include "Arduino.h"
#include "Node.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
int *_channelList;
Node::Node(boolean isBase, int *channelList)
{
_isBase = isBase;
int i=0;
while(channelList[i]!='\0'){
_channelList[i] = channelList[i];
i++;
}
_channelList[i+1] = '\0';
}
boolean Node::isBase(){
return _isBase;
}
int* Node::getChannelList(){
return _channelList;
}
Justin Time's answer is the correct way to implement this in C++ (using arrays and vectors natively handled by C++)
I just have to add this solution, which is the correct way to implement what you tried to do in C (i.e. using char arrays).
There are two problems in your code
_channelList is NOT tied to the Node object, but is a somewhat static member.
_channelList is never allocated, so it points to nothing.
not a real problem, but usually '\0' is the string terminator. Ok, it maps to 0, but you should just use a 0 here
There are two solutions here. The first one is to give _channelList a fixed MAXIMUM size (maximum means that if the passed channelList is shorter you will get a shorter list, ok, but the allocated memory will still be the maximum one).
// File Node.h
#define MAXIMUM_CHANNELS 10
class Node {
public:
Node(boolean isBase, int *channelList);
boolean isBase();
int* getChannelList();
private:
int _channelList[MAXIMUM_CHANNELS + 1]; // Last one is the terminator
};
// File Node.cpp
include "Arduino.h"
#include "Node.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
Node::Node(boolean isBase, int *channelList)
{
_isBase = isBase;
int channelListLength;
// Get channel list lenght
for (channelListLength = 0; channelList[channelListLength] != 0; channelListLength++);
if (channelListLength > MAXIMUM_CHANNELS)
channelListLength = MAXIMUM_CHANNELS;
int i;
for (i = 0; i < channelListLength; i++)
_channelList[i] = channelList[i];
_channelList[channelListLength] = 0; // Enforce the last one to be a 0
}
boolean Node::isBase(){
return _isBase;
}
int* Node::getChannelList(){
return _channelList;
}
The second method dynamically allocates the memory for the array. You should, however, dispose it when you are done with the object (in the destructor). This means that if you create the Node variable you are ok (for instance, Node mynode;). If, however, you dynamically allocate it (with Node *p_mynode = new Node(); you will need to call a delete on it when you are done.
// File Node.h
class Node {
public:
Node(boolean isBase, int *channelList);
~Node(); // destructor (called at object destruction)
boolean isBase();
int* getChannelList();
private:
int *_channelList;
};
// File Node.cpp
include "Arduino.h"
#include "Node.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
Node::Node(boolean isBase, int *channelList)
{
_isBase = isBase;
int channelListLength;
// Get channel list lenght
for (channelListLength = 0; channelList[channelListLength] != 0; channelListLength++);
_channelList = (int*)malloc((channelListLength+1)*sizeof(int));
if (_channelList != NULL)
{
int i;
for (i = 0; i <= channelListLength; i++)
_channelList[i] = channelList[i];
// No need to enforce the last one to be a 0
}
}
~Node()
{
free(_channelList);
}
boolean Node::isBase(){
return _isBase;
}
int* Node::getChannelList(){
return _channelList;
}
Note, however, that if the malloc fails you will have a NULL pointer. In theory it should not fail, unless you run out of ram...
Just one more thing. Using a 0-terminated int array is not a good idea, because if you have something like this { 15, 3, 0, 5, 10 } and then the terminator you will just get the first two numbers. It would be MUCH better to explicitely tell the array size (and save it in the object)
// File Node.h
class Node {
[...]
private:
int *_channelList;
int _channelListLength;
};
// File Node.cpp
Node::Node(boolean isBase, int *channelList, int channelListLength)
{
_isBase = isBase;
_channelListLength = channelListLength;
_channelList = (int*)malloc((_channelListLength)*sizeof(int));
if (_channelList != NULL)
{
int i;
for (i = 0; i <= _channelListLength; i++)
_channelList[i] = channelList[i];
}
}
...
int Node::getChannelListLength(){
return _channelListLength;
}
Assuming that channelList is null-terminated, and that _channelList is supposed to be a class member, you could try this:
#include <algorithm> // For std::copy().
// ...
template<size_t N> Node::Node(bool isBase, const int (&channelList)[N]) : _isBase(isBase)
{
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
Unless I'm missing something, that should take a C-style int array of any size, and copy it into _channelList. If the passed array is larger than _channelList, it may cause issues. If possible, you would be better off using a std::array if the size is predetermined, or a std::vector if it isn't.
If the size is fixed:
#include <array>
#include <algorithm> // For std::copy() and std::fill().
const size_t _sz = [whatever];
// Simple way of setting size, but not the best. See below.
class Node
{
bool _isBase;
std::array<int, _sz> _channelList;
public:
Node(bool isBase, const int (&channelList)[_sz]);
Node(bool isBase, const std::array<int, _sz>& channelList);
// ...
};
/* Alternatively, you can code the size into the class as a magic number (not a good idea),
* or better yet, make it a template parameter for the class:
* template<size_t _sz> class Node
* {
* bool _isBase;
* std::array<int, _sz> _channelList;
*
* public:
* Node(/ * parameters * /);
*
* template<size_t N>
* Node(/ * parameters * /);
* // ...
* };
* When using the class, you would declare an instance as "Node<SIZE>", where "SIZE" is the
* desired size.
*
* Note that if you make the size a template parameter, and define the member functions
* outside of the class' definition, you have to put the same template at the start of each
* member function:
* template<size_t _sz> Node<_sz>::Node(/ * parameters * /)
* {
* // ...
* }
* This also applies to templated member functions, which will have two sets of template
* parameters.
* template<size_t _sz> template<size_t N> Node<_sz>::Node(/ * parameters * /)
* {
* // ...
* }
*/
// Constructor initialising from C array, if you need to work with preexisting code.
Node::Node(bool isBase, const int (&channelList)[_sz]) : _isBase(isBase)
{
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
// Constructor using std::array.
Node::Node(bool isBase, const std::array<int, _sz>& channelList)
: _isBase(isBase), _channelList(channelList)
{
// Initialisation list handles everything.
}
// Note, however, that this will cause issues if the size of channelList doesn't
// necessarily match the size of _channelList. To solve this, we can change Node as
// follows:
// (Note that delegation requires a C++11-compatible compiler.)
#include <type_traits> // For std::integral_constant, std::true_type, and std::false_type.
class Node {
bool _isBase;
std::array<int, _sz> _channelList;
// Actual constructors (C-style array):
template<size_t N>
Node(std::true_type x, bool isBase, const int (&channelList)[N]);
template<size_t N>
Node(std::false_type x, bool isBase, const int (&channelList)[N]);
// Actual constructors (std::array):
template<size_t N>
Node(std::true_type x, bool isBase, const std::array<int, N>& channelList);
template<size_t N>
Node(std::false_type x, bool isBase, const std::array<int, N>& channelList);
public:
// Public constructors, delegate to one of the actual constructors.
// C-style array:
template<size_t N>
Node(bool isBase, const int (&channelList)[N]);
// std::array:
template<size_t N>
Node(bool isBase, const std::array<int, N>& channelList);
// ...
};
/* Now, these constructors are easy enough to make.
* I'll provide an example using C-style arrays. To make versions that take a
* std::array instead, change the parameter:
* const int (&channelList)[N]
* to:
* const std::array<int, N>& channelList
* The constructors should work properly with either kind of array.
*/
// Check if passed array is smaller than or equal to _sz, or if it's larger..
template<size_t N> Node::Node(bool isBase, const int (&channelList)[N])
: Node(std::integral_constant<bool, N <= _sz>{}, isBase, channelList) { }
// N is smaller than or equal to _sz.
template<size_t N> Node::Node(std::true_type x, bool isBase, const int (&channelList)[N])
: _isBase(isBase)
{
// Copy channelList into _channelList.
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
// Fill empty space at the end of _channelList.
std::fill(&(_channelList[N]), std::end(_channelList), '\0');
}
// N is larger than _sz.
template<size_t N> Node::Node(std::false_type x, bool isBase, const int (&channelList)[N])
{
// Handle appropriately.
}
This should allow you to get the functionality you want. [Note that you can also use the above delegation, true_type, and false_type constructors to fill C-style arrays as well as std::arrays, if you need to use them.]
If the size isn't fixed:
#include <vector>
#include <algorithm>
class Node {
bool _isBase;
std::vector<int> _channelList;
public:
template<size_t N>
Node(bool isBase, const int (&channelList)[N]);
// ...
};
template<size_t N> Node::Node(bool isBase, const int (&channelList)[N]) : _isBase(isBase)
{
_channelList.assign(std::begin(channelList), std::end(channelList));
}
// You can also define a constructor that takes a std::array<int, N>, if you
// so desire. Again, the only change needed is the parameter itself.
As a vector's length can be changed at runtime, we can use vector::assign to allocate space and store the entirety of channelList.
Regardless of whether _channelList is stored as a C array, std::array, or std::vector, it's relatively easy to define getters and setters.
Getter (entire thing):
// All of the following use this class definition, with comments identifying which
// functions use which parts.
// Note that the trailing "const" in each getter signature indicates that the function
// cannot be used to modify the instance. It's optional, but useful to include.
class Node {
// Return C array (either way).
int _channelListC[_sz];
// Return std::array.
std::array<int, _sz> _channelListSArr;
// Return std::vector.
std::vector<int> _channelListSVec;
// Return C array the readable way.
typedef int _channelListC_t[_sz];
// C++11 alternative typedef:
using _channelListC_t = decltype(_channelList);
// The C++11 version is safer, as "decltype(_channelList)" won't break if you change
// _channelList's implementation.
// If you need to return the entire array, it may be a good idea to make this a public
// typedef, so it's easier & safer to declare a variable you can return it to.
public:
// Return C array the ugly way.
const int (&getChannelListCUgly() const)[_sz];
// Return C array the readable way.
const _channelListC_t& getChannelListCReadable() const;
// Return C array the readable C++11 way.
auto getChannelListCReadableCPP11() const -> const int(&)[_sz];
// Return std::array.
const std::array<int, _sz>& getChannelListSArr() const;
// Return std::vector.
const std::vector<int>& getChannelListSVec() const;
};
// Return C array:
/* Note that you can't return an array from a function. However, you can return a pointer
* or reference to an array, depending on whether you use * or & in the signature.
*/
// The ugly way:
const int (&Node::getChannelListCUgly() const)[_sz]
{
return _channelList;
}
// The readable way:
const Node::_channelListC_t& Node::getChannelListCReadable() const
{
return _channelList;
}
// The new readable way, as of C++11:
auto getChannelListCReadableCPP11() const -> const int(&)[_sz]
{
return _channelList;
}
// Return std::array:
const std::array<int, _sz>& Node::getChannelListSArr() const
{
return _channelList;
}
// Return std:;vector:
const std::vector<int>& getChannelListSVec() const
{
return _channelList;
}
Note that to my knowledge, a C-style array returned in this manner must be stored in a reference variable.
Node::_channelListC_t& arr = nodeInstance.getChannelListCUgly();
Getter (single element):
// C array or std::array:
int Node::getChannelListArrElement(int index) const
{
if (index < _sz)
{
// index is valid, return element.
return _channelList[index];
}
else
{
// index >= _sz, and is invalid.
// Handle accordingly.
}
}
// std::vector:
int Node::getChannelListVecElement(int index) const
{
if (index < _channelList.size())
{
// index is valid.
return _channelList[index];
}
else
{
// index is invalid.
// Handle accordingly.
}
}
You can define a setter for the entire thing using the constructors above. I would suggest using std::fill() to erase the contents of _channelList first, then copying the new array into _channelList. You can define a setter for single elements using the single-element getter as a basis.
Setter (entire thing):
// Array (either type):
// "CHANNEL_LIST_TYPE[N] channelList" is either "const int (&channelList)[N]" or
// "std::array<int, N>& channelList". Remember to replace it with the correct one in the
// actual code.
// Public function.
template<size_t N>
void Node::setChannelListArr(CHANNEL_LIST_TYPE[N] channelList)
{
setChannelListArr(std::integral_constant<bool, N <= _sz>{}, channelList);
}
// Private function, N <= _sz.
template<size_t N>
void Node::setChannelListArr(std::true_type x, CHANNEL_LIST_TYPE[N] channelList)
{
std::fill(std::begin(_channelList), std::end(_channelList), '\0');
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
// Private function, N > _sz.
template<size_t N>
void Node::setChannelListArr(std::false_type x, CHANNEL_LIST_TYPE[N] channelList)
{
// channelList is too large. Handle appropriately.
}
// std::vector:
// "CHANNEL_LIST_TYPE[N]" is used as above, and should be replaced in your actual code.
// Also note that you can easily modify this function to accept another vector, by
// removing the template, making the parameter "const std::vector<int>& channelList", and
// using "channelList.size()" in place of "N" when calling resize().
template<size_t N>
void Node::setChannelListVec(CHANNEL_LIST_TYPE[N] channelList)
{
_channelList.resize(N); // Resize _channelList to have N elements.
std::fill(std::begin(_channelList), std::end(_channelList), '\0');
std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList));
}
Setter (single element):
// Array (either type):
void Node::setChannelListArrElement(int index, int value)
{
if (index < _sz)
{
_channelList[index] = value;
}
else
{
// index is invalid. Handle accordingly.
}
}
// std::vector:
void Node::setChannelListVecElement(int index, int value)
{
if (index < _channelList.size())
{
_channelList[index] = value;
}
else
{
// index is invalid. Handle accordingly.
}
}
// Alternative std::vector setter:
void Node::setChannelListVecElement2(int index, int value)
{
if (index >= _channelList.size())
{
// index is out of bounds. Resize vector to fit it.
_channelList.resize(index + 1, '\0');
}
// Modify element.
_channelList[index] = value;
}
Note that this answer assumes that channelList is null-terminated, as it appears to be. If channelList isn't null-terminated, but you want to stop filling _channelList at the first null element, then you'll have to do a bit more work, likely using your while loop.
You can find working examples of most of the above here. It's a bit of a mess, since it's just a quick program I used to test various things while typing up this answer.
[My apologies for any typoes and/or errors I may have missed. I believe I caught them, but there may still be some there.]
[Edit: Added a note about using fixed-size template constructors with C arrays. Added the C++11 trailing return type way of returning a reference to an array. Added a simple, working example.]
[Edit: Added an additional single-element setter for a vector.]

How to initialise a floating point array at compile time?

I have found two good approaches to initialise integral arrays at compile times here and here.
Unfortunately, neither can be converted to initialise a float array straightforward; I find that I am not fit enough in template metaprogramming to solve this through trial-and-error.
First let me declare a use-case:
constexpr unsigned int SineLength = 360u;
constexpr unsigned int ArrayLength = SineLength+(SineLength/4u);
constexpr double PI = 3.1415926535;
float array[ArrayLength];
void fillArray(unsigned int length)
{
for(unsigned int i = 0u; i < length; ++i)
array[i] = sin(double(i)*PI/180.*360./double(SineLength));
}
As you can see, as far as the availability of information goes, this array could be declared constexpr.
However, for the first approach linked, the generator function f would have to look like this:
constexpr float f(unsigned int i)
{
return sin(double(i)*PI/180.*360./double(SineLength));
}
And that means that a template argument of type float is needed. Which is not allowed.
Now, the first idea that springs to mind would be to store the float in an int variable - nothing happens to the array indices after their calculation, so pretending that they were of another type than they are (as long as byte-length is equal) is perfectly fine.
But see:
constexpr int f(unsigned int i)
{
float output = sin(double(i)*PI/180.*360./double(SineLength));
return *(int*)&output;
}
is not a valid constexpr, as it contains more than the return statement.
constexpr int f(unsigned int i)
{
return reinterpret_cast<int>(sin(double(i)*PI/180.*360./double(SineLength)));
}
does not work either; even though one might think that reinterpret_cast does exactly what is needed here (namely nothing), it apparently only works on pointers.
Following the second approach, the generator function would look slightly different:
template<size_t index> struct f
{
enum : float{ value = sin(double(index)*PI/180.*360./double(SineLength)) };
};
With what is essentially the same problem: That enum cannot be of type float and the type cannot be masked as int.
Now, even though I have only approached the problem on the path of "pretend the float is an int", I do not actually like that path (aside from it not working). I would much prefer a way that actually handled the float as float (and would just as well handle a double as double), but I see no way to get around the type restriction imposed.
Sadly, there are many questions about this issue, which always refer to integral types, swamping the search for this specialised issue. Similarly, questions about masking one type as the other typically do not consider the restrictions of a constexpr or template parameter environment.
See [1][2][3] and [4][5] etc.
Assuming your actual goal is to have a concise way to initialize an array of floating point numbers and it isn't necessarily spelled float array[N] or double array[N] but rather std::array<float, N> array or std::array<double, N> array this can be done.
The significance of the type of array is that std::array<T, N> can be copied - unlike T[N]. If it can be copied, you can obtain the content of the array from a function call, e.g.:
constexpr std::array<float, ArrayLength> array = fillArray<N>();
How does that help us? Well, when we can call a function taking an integer as an argument, we can use std::make_index_sequence<N> to give use a compile-time sequence of std::size_t from 0 to N-1. If we have that, we can initialize an array easily with a formula based on the index like this:
constexpr double const_sin(double x) { return x * 3.1; } // dummy...
template <std::size_t... I>
constexpr std::array<float, sizeof...(I)> fillArray(std::index_sequence<I...>) {
return std::array<float, sizeof...(I)>{
const_sin(double(I)*M_PI/180.*360./double(SineLength))...
};
}
template <std::size_t N>
constexpr std::array<float, N> fillArray() {
return fillArray(std::make_index_sequence<N>{});
}
Assuming the function used to initialize the array elements is actually a constexpr expression, this approach can generate a constexpr. The function const_sin() which is there just for demonstration purpose does that but it, obviously, doesn't compute a reasonable approximation of sin(x).
The comments indicate that the answer so far doesn't quite explain what's going on. So, let's break it down into digestible parts:
The goal is to produce a constexpr array filled with suitable sequence of values. However, the size of the array should be easily changeable by adjusting just the array size N. That is, conceptually, the objective is to create
constexpr float array[N] = { f(0), f(1), ..., f(N-1) };
Where f() is a suitable function producing a constexpr. For example, f() could be defined as
constexpr float f(int i) {
return const_sin(double(i) * M_PI / 180.0 * 360.0 / double(Length);
}
However, typing in the calls to f(0), f(1), etc. would need to change with every change of N. So, essentially the same as the above declaration should be done but without extra typing.
The first step towards the solution is to replace float[N] by std:array<float, N>: built-in arrays cannot be copied while std::array<float, N> can be copied. That is, the initialization could be delegated to to a function parameterized by N. That is, we'd use
template <std::size_t N>
constexpr std::array<float, N> fillArray() {
// some magic explained below goes here
}
constexpr std::array<float, N> array = fillArray<N>();
Within the function we can't simply loop over the array because the non-const subscript operator isn't a constexpr. Instead, the array needs to be initialized upon creation. If we had a parameter pack std::size_t... I which represented the sequence 0, 1, .., N-1 we could just do
std::array<float, N>{ f(I)... };
as the expansion would effectively become equivalent to typing
std::array<float, N>{ f(0), f(1), .., f(N-1) };
So the question becomes: how to get such a parameter pack? I don't think it can be obtained directly in the function but it can be obtained by calling another function with a suitable parameter.
The using alias std::make_index_sequence<N> is an alias for the type std::index_sequence<0, 1, .., N-1>. The details of the implementation are a bit arcane but std::make_index_sequence<N>, std::index_sequence<...>, and friends are part of C++14 (they were proposed by N3493 based on, e.g., on this answer from me). That is, all we need to do is call an auxiliary function with a parameter of type std::index_sequence<...> and get the parameter pack from there:
template <std::size_t...I>
constexpr std::array<float, sizeof...(I)>
fillArray(std::index_sequence<I...>) {
return std::array<float, sizeof...(I)>{ f(I)... };
}
template <std::size_t N>
constexpr std::array<float, N> fillArray() {
return fillArray(std::make_index_sequence<N>{});
}
The [unnamed] parameter to this function is only used to have the parameter pack std::size_t... I be deduced.
Here's a working example that generates a table of sin values, and that you can easily adapt to logarithm tables by passing a different function object
#include <array> // array
#include <cmath> // sin
#include <cstddef> // size_t
#include <utility> // index_sequence, make_index_sequence
#include <iostream>
namespace detail {
template<class Function, std::size_t... Indices>
constexpr auto make_array_helper(Function f, std::index_sequence<Indices...>)
-> std::array<decltype(f(std::size_t{})), sizeof...(Indices)>
{
return {{ f(Indices)... }};
}
} // namespace detail
template<std::size_t N, class Function>
constexpr auto make_array(Function f)
{
return detail::make_array_helper(f, std::make_index_sequence<N>{});
}
static auto const pi = std::acos(-1);
static auto const make_sin = [](int x) { return std::sin(pi * x / 180.0); };
static auto const sin_table = make_array<360>(make_sin);
int main()
{
for (auto elem : sin_table)
std::cout << elem << "\n";
}
Live Example.
Note that you need to use -stdlib=libc++ because libstdc++ has a pretty inefficient implementation of index_sequence.
Also note that you need a constexpr function object (both pi and std::sin are not constexpr) to initialize the array truly at compile-time rather than at program initialization.
There are a few problems to overcome if you want to initialise a floating point array at compile time:
std::array is a little broken in that the operator[] is not constexpr in the case of a mutable constexpr std::array (I believe this will be fixed in the next release of the standard).
the functions in std::math are not marked constexpr!
I had a similar problem domain recently. I wanted to create an accurate but fast version of sin(x).
I decided to see if it could be done with a constexpr cache with interpolation to get speed without loss of accuracy.
An advantage of making the cache constexpr is that the calculation of sin(x) for a value known at compile-time is that the sin is pre-computed and simply exists in the code as an immediate register load! In the worst case of a runtime argument, it's merely a constant array lookup followed by w weighted average.
This code will need to be compiled with -fconstexpr-steps=2000000 on clang, or the equivalent in windows.
enjoy:
#include <iostream>
#include <cmath>
#include <utility>
#include <cassert>
#include <string>
#include <vector>
namespace cpputil {
// a fully constexpr version of array that allows incomplete
// construction
template<size_t N, class T>
struct array
{
// public constructor defers to internal one for
// conditional handling of missing arguments
constexpr array(std::initializer_list<T> list)
: array(list, std::make_index_sequence<N>())
{
}
constexpr T& operator[](size_t i) noexcept {
assert(i < N);
return _data[i];
}
constexpr const T& operator[](size_t i) const noexcept {
assert(i < N);
return _data[i];
}
constexpr T& at(size_t i) noexcept {
assert(i < N);
return _data[i];
}
constexpr const T& at(size_t i) const noexcept {
assert(i < N);
return _data[i];
}
constexpr T* begin() {
return std::addressof(_data[0]);
}
constexpr const T* begin() const {
return std::addressof(_data[0]);
}
constexpr T* end() {
// todo: maybe use std::addressof and disable compiler warnings
// about array bounds that result
return &_data[N];
}
constexpr const T* end() const {
return &_data[N];
}
constexpr size_t size() const {
return N;
}
private:
T _data[N];
private:
// construct each element from the initialiser list if present
// if not, default-construct
template<size_t...Is>
constexpr array(std::initializer_list<T> list, std::integer_sequence<size_t, Is...>)
: _data {
(
Is >= list.size()
?
T()
:
std::move(*(std::next(list.begin(), Is)))
)...
}
{
}
};
// convenience printer
template<size_t N, class T>
inline std::ostream& operator<<(std::ostream& os, const array<N, T>& a)
{
os << "[";
auto sep = " ";
for (const auto& i : a) {
os << sep << i;
sep = ", ";
}
return os << " ]";
}
}
namespace trig
{
constexpr double pi() {
return M_PI;
}
template<class T>
auto constexpr to_radians(T degs) {
return degs / 180.0 * pi();
}
// compile-time computation of a factorial
constexpr double factorial(size_t x) {
double result = 1.0;
for (int i = 2 ; i <= x ; ++i)
result *= double(i);
return result;
}
// compile-time replacement for std::pow
constexpr double power(double x, size_t n)
{
double result = 1;
while (n--) {
result *= x;
}
return result;
}
// compute a term in a taylor expansion at compile time
constexpr double taylor_term(double x, size_t i)
{
int powers = 1 + (2 * i);
double top = power(x, powers);
double bottom = factorial(powers);
auto term = top / bottom;
if (i % 2 == 1)
term = -term;
return term;
}
// compute the sin(x) using `terms` terms in the taylor expansion
constexpr double taylor_expansion(double x, size_t terms)
{
auto result = x;
for (int term = 1 ; term < terms ; ++term)
{
result += taylor_term(x, term);
}
return result;
}
// compute our interpolatable table as a constexpr
template<size_t N = 1024>
struct sin_table : cpputil::array<N, double>
{
static constexpr size_t cache_size = N;
static constexpr double step_size = (pi() / 2) / cache_size;
static constexpr double _360 = pi() * 2;
static constexpr double _270 = pi() * 1.5;
static constexpr double _180 = pi();
static constexpr double _90 = pi() / 2;
constexpr sin_table()
: cpputil::array<N, double>({})
{
for(int slot = 0 ; slot < cache_size ; ++slot)
{
double val = trig::taylor_expansion(step_size * slot, 20);
(*this)[slot] = val;
}
}
double checked_interp_fw(double rads) const {
size_t slot0 = size_t(rads / step_size);
auto v0 = (slot0 >= this->size()) ? 1.0 : (*this)[slot0];
size_t slot1 = slot0 + 1;
auto v1 = (slot1 >= this->size()) ? 1.0 : (*this)[slot1];
auto ratio = (rads - (slot0 * step_size)) / step_size;
return (v1 * ratio) + (v0 * (1.0-ratio));
}
double interpolate(double rads) const
{
rads = std::fmod(rads, _360);
if (rads < 0)
rads = std::fmod(_360 - rads, _360);
if (rads < _90) {
return checked_interp_fw(rads);
}
else if (rads < _180) {
return checked_interp_fw(_90 - (rads - _90));
}
else if (rads < _270) {
return -checked_interp_fw(rads - _180);
}
else {
return -checked_interp_fw(_90 - (rads - _270));
}
}
};
double sine(double x)
{
if (x < 0) {
return -sine(-x);
}
else {
constexpr sin_table<> table;
return table.interpolate(x);
}
}
}
void check(float degs) {
using namespace std;
cout << "checking : " << degs << endl;
auto mysin = trig::sine(trig::to_radians(degs));
auto stdsin = std::sin(trig::to_radians(degs));
auto error = stdsin - mysin;
cout << "mine=" << mysin << ", std=" << stdsin << ", error=" << error << endl;
cout << endl;
}
auto main() -> int
{
check(0.5);
check(30);
check(45.4);
check(90);
check(151);
check(180);
check(195);
check(89.5);
check(91);
check(270);
check(305);
check(360);
return 0;
}
expected output:
checking : 0.5
mine=0.00872653, std=0.00872654, error=2.15177e-09
checking : 30
mine=0.5, std=0.5, error=1.30766e-07
checking : 45.4
mine=0.712026, std=0.712026, error=2.07233e-07
checking : 90
mine=1, std=1, error=0
checking : 151
mine=0.48481, std=0.48481, error=2.42041e-08
checking : 180
mine=-0, std=1.22465e-16, error=1.22465e-16
checking : 195
mine=-0.258819, std=-0.258819, error=-6.76265e-08
checking : 89.5
mine=0.999962, std=0.999962, error=2.5215e-07
checking : 91
mine=0.999847, std=0.999848, error=2.76519e-07
checking : 270
mine=-1, std=-1, error=0
checking : 305
mine=-0.819152, std=-0.819152, error=-1.66545e-07
checking : 360
mine=0, std=-2.44929e-16, error=-2.44929e-16
I am just keeping this answer for documentation. As the comments say, I was mislead by gcc being permissive. It fails, when f(42) is used e.g. as a template parameter like this:
std::array<int, f(42)> asdf;
sorry, this was not a solution
Separate the calculation of your float and the conversion to an int in two different constexpr functions:
constexpr int floatAsInt(float float_val) {
return *(int*)&float_val;
}
constexpr int f(unsigned int i) {
return floatAsInt(sin(double(i)*PI/180.*360./double(SineLength)));
}

How to make this "template / constexpr" construct more elegant / less verbose?

I have this pseudo bitfield implementation:
class Field {
public:
constexpr Field(int i, int s) : index(i), size(s) {}
constexpr Field(const Field & prev, int s) : index(prev.index + prev.size), size(s) {}
int index, size;
};
#define FIELD(name, i, s) constexpr static const Field name = {i, s};
template<typename T = quint32>
class Flags {
public:
Flags(T d = 0) : data(d) {}
inline T readField(const Field & f) {
return (data & getMask(f.index, f.size)) >> f.index;
}
inline void writeField(const Field & f, T val) {
data = (data & setMask(f.index, f.size)) | (val << f.index);
}
private:
static constexpr T getMask(int i, int size) {
return ((1 << size) - 1) << i;
}
static constexpr T setMask(int pos, int size) {
return ~getMask(pos, size);
}
T data;
};
However, it is quite verbose to use in its present form:
struct Test {
Flags<> flags;
FIELD(one, 0, 1)
FIELD(two, one, 2)
};
Test t;
t.flags.readField(t.one);
t.flags.writeField(t.one, 1);
I would like to make it more elegant, so instead of the syntax above I can simply do this:
t.one.read();
t.one.write(1);
The way I tried to do this is have a Flags & for each Field and implement read() and write() methods which use the Flags it targets internally.
This however requires that the Field is made a template as well, which increased the verbosity further, now a T has to be specified for the fields as well.
I tried having T be specified implicitly using a Flags<T>::makeField() but it soon became a mess of incompatibility between constexprt, static and regular members and methods, auto and whatnot, so after going in circles finally decided to seek an advice from people with more experience.
Naturally, there is the requirement that Fields do not take up runtime storage and as many as possible expressions are resolved during compile.
Having absolutely no idea what your intent is, my first suggestion is to simply use a bitfield. It's a thousand times simpler/faster/etc.
struct Test {
unsigned long long one : 1;
unsigned long long one : 2;
};
However, if you really want a class, I made a FieldReference class that appears to vaguely match what you're doing.
Class:
#include <cassert>
#include <type_traits>
#include <cstddef>
template<class T, size_t offset_, size_t size_>
struct FieldReference {
static const size_t offset = offset_;
static const size_t size = size_;
static const size_t mask = ~T(((~0)<<offset<<size)|((1<<offset)-1));
explicit FieldReference(T& f) :flags(&f) {}
operator T() const {return (flags[0]&mask)>>offset;}
FieldReference& operator=(T v) {
assert((v&~(mask>>offset))==0);
flags[0] &= ~mask;
flags[0] |= (v<<offset);
return *this;
}
private:
T* flags;
};
#define FIRSTFIELD(Flags,Name,Size) \
auto Name() -> FieldReference<decltype(Flags),0,Size> {return FieldReference<decltype(Flags),0,Size>(Flags);} \
FieldReference<std::add_const<decltype(Flags)>::type,0,Size> Name() const {return FieldReference<std::add_const<decltype(Flags)>::type,0,Size>(Flags);}
#define FIELD(Flags,Name,Previous,Size) \
auto Name() -> FieldReference<decltype(Flags),decltype(Previous())::offset,Size> {return FieldReference<decltype(Flags),decltype(Previous())::offset,Size>(Flags);} \
auto Name() const -> FieldReference<std::add_const<decltype(Flags)>::type,decltype(this->Previous())::offset,Size> {return FieldReference<std::add_const<decltype(Flags)>::type,decltype(Previous())::offset,Size>(Flags);}
Usage:
struct Test {
unsigned long long flags = 0;
FIRSTFIELD(flags,one,1);
FIELD(flags,two,one,2);
};
#include <iostream>
int main() {
Test t;
t.one() = 1; //That seems less verbose
t.two() = 3;
std::cout << t.two();
return 0;
}
http://coliru.stacked-crooked.com/a/c027d9829ce05119
The fields don't take any space whatsoever except while you're working on them, and even then they only take the space of a single pointer. All offsets and sizes and masks are calculated at compile time, so this should be faster than your code too.
Here is how to get really close to what you want. Please, keep in mind that you said that the implementation can be ugly. :)
template<class T, T mask, T bitpos>
class Field {
T &d_t;
public:
Field(T &t) : d_t(t) {}
T read() const {
return (d_t & mask) >> bitpos;
}
void write(T const &t) {
d_t = (d_t & ~mask) | (t << bitpos);
}
};
#define BTFDENUMDECL1(name, width) name##start
#define BTFDENUMDECL2(name, width, ...) name##start, name##end = name##start + width - 1, BTFDENUMDECL1(__VA_ARGS__)
#define BTFDENUMDECL3(name, width, ...) name##start, name##end = name##start + width - 1, BTFDENUMDECL2(__VA_ARGS__)
#define BTFDENUMDECL4(name, width, ...) name##start, name##end = name##start + width - 1, BTFDENUMDECL3( __VA_ARGS__)
#define BTFNMEMBER1(field, name, width) auto name() { \
return Field<decltype(field), ((1 << width) - 1) << name##start, name##start>(field); }
#define BTFNMEMBER2(field, name, width, ...) BTFNMEMBER1(field, name, width) BTFNMEMBER1(field, __VA_ARGS__)
#define BTFNMEMBER3(field, name, width, ...) BTFNMEMBER1(field, name, width) BTFNMEMBER2(field, __VA_ARGS__)
#define BTFNMEMBER4(field, name, width, ...) BTFNMEMBER1(field, name, width) BTFNMEMBER3(field, __VA_ARGS__)
#define GET_MACRO(_1,_1_,_2,_2_,_3,_3_,_4,_4_, NAME, ...) NAME
#define BITFIELDS(field, ...) \
private: uint32_t field; \
enum E##flags { GET_MACRO(__VA_ARGS__, BTFDENUMDECL4,0, BTFDENUMDECL3,0, BTFDENUMDECL2,0, BTFDENUMDECL1,0)(__VA_ARGS__) }; \
public: \
GET_MACRO(__VA_ARGS__, BTFNMEMBER4,0, BTFNMEMBER3,0, BTFNMEMBER2,0, BTFNMEMBER1,0)(field, __VA_ARGS__)
This is for C++14, but could be "cut down" to C++11. It supports up to 4 fields, but you can just add more with simple cloning of BTFDENUMDECL and BTFNMEMBER macros and updating GET_MACRO. You would use it like:
struct Test {
BITFIELDS(flags,
one, 1,
two, 2,
three, 3
);
};
and in the code:
Test test;
test.one().write(0);
std::cout << test.one().read() << std::endl;
Thus, there are only the parentheses added to your desired syntax.
A quick explanation: we are (ob)using the variable number of arguments for macros. We take two parameters (from the variable arguments "list") at a time - most examples you'll find take one parameter at a time, so this will be a little unusual. In the BITFIELDS macro, we first define an enum with the bit positions of the fields, then a function for each field. Functions for fields return a proxy which has the read() and write() methods, which uses the bit positions defined in the enum.
This is the bare-bones implementation. You can add your own bells and whistles, like checking for range in write()), another macro for giving a type for the field (like BITFIELDST(T, field, ...)), etc.
For C++98, you would have to refactor it a little (to remove usage of __VA_ARGS__), and use it by giving the number of parameters in the name of the macro "call", like BITFIELDS4.
Doesn't look like anyone else is biting so I'll just mention the two methods that came to mind.
I think the key here is a field which is able to modify a specific value without itself taking up any storage space. So the language features which stand out to accomplish this would be:
Anonymous union which gives test.one.read() kind of syntax.
Empty Base Optimization which would give test.one().read() kind of syntax.
A quick example of the former without the actual bit logic - all Fields in this example just modify the entire value. The bitwise logic would be trivial:
template< typename T >
struct Bitmask
{
T m_val;
};
template< typename BITMASK, int INDEX >
struct Field : private BITMASK
{
int read() const { return BITMASK::m_val; }
void write( int i ) { BITMASK::m_val = i; }
};
struct Test
{
typedef Bitmask<int> Flags;
union
{
Flags m_flags;
Field<Flags,0> one;
Field<Flags,1> two;
Field<Flags,2> three;
};
};
This meets your specific usage but with the caveat that the Field is also templated. Just as a side note I really think that however things are done it should really be test.m_flags.one.read() or similar since if the bits are being uniquely but generically named then this allows any class with a Flags instance to have multiple of them without issue.
The Empty Base Optimization with functions I haven't mocked up but the function would return an accessor object - much like the Field in your example but the required 'Flags&' parameter would already be bound.
The Empty Base also may require single inheritance and some casting. On the plus side I think it could be made to support exactly the number of bits in the bitmask. So if 3 bits were needed, it might be stored as an unsigned char but only functions one(), two() and three() would be present.
If you would like, and if I have some time, I could mock up this example too.
As far as I can tell both of these techniques should work, so I'd be interested to know if they aren't portable and if so, for what reasons.
EDIT: A quick read over cppreference's section on unions indicates that reading from a non-active member of a union isn't supported by the standard. However the major compilers support it. So theres one issue with the approach.
First, if you want to achieve such effect:
int main() {
Test t;
cout << t.one.read() << endl;
t.one.write(1);
cout << t.one.read() << endl;
}
You have to inform one and two that they will manipulate the flags - so the first change - added flags as argument to FIELD:
struct Test {
Flags<> flags;
FIELD(one, 0, 1, flags);
// ^^^^^
FIELD(two, AFTER(one), 2, flags);
// ^^^^^^^^^^
};
Other change is that I modified your way of applying previous flag as index to next flag - see use of AFTER macro. I believe it is more readable now, and it simplifies my proposal. AFTER'll be presented after, first let discuss the more important magic.
So, I introduced the FieldManip class, to manipulate the given flags:
template <typename Flags, int i, int s>
class FieldManip {
public:
constexpr static const Field field = {i, s};
FieldManip(Flags* flags) : flags(flags) {}
auto read()
{
return flags->readField(field);
}
template <typename T>
void write(T value)
{
flags->writeField(field, value);
}
private:
Flags* flags;
};
It costs the extra memory of size: sizeof(Flags*), but good compiler shall optimize any CPU overhead. I am afraid there is no other way than paying this extra memory, unless you want to relax your requirement - so one and two could be the member functions, not member variables...
So, the definition of new FLAGS is straightforward:
#define FIELD(name, i, s, flags) \
FieldManip<decltype(flags), i, s> name{&flags}
It's time to explain AFTER:
First, some simplification of Field, extra constructor removed and added after standalone function:
class Field {
public:
constexpr Field(int i, int s) : index(i), size(s) {}
int index, size;
};
constexpr int after(const Field& prev) { return prev.index + prev.size; }
But in macro we have FieldManip not Field - so next function needed:
template <typename FieldManip>
constexpr int after() { return after(FieldManip::field); }
And the AFTER:
#define AFTER(fieldmanip) after<decltype(fieldmanip)>()
Why call methods?
First, a slightly different Flags:
template<class C, size_t N=0, class D=uint32_t>
struct Flags {
Flags(Flags const&)=default;
Flags():data() {} // remember to zero!
Flags(D raw):data(raw) {}
template<unsigned start, unsigned width>
constexpr void set( D bits ) {
D m = mask<start, width>();
data &= ~m;
data |= (bits<<start)&m;
}
template<unsigned start, unsigned width>
constexpr D get( D bits ) const {
D m = mask<start, width>();
return (data&m)>>start;
}
private:
template<unsigned start, unsigned width>
static constexpr D mask() {
return ((1<<(width)-1)<<start;
}
D data;
};
Our Flags is now typed on the type of the container it is in.
If you want more than one set of Flags in a container, pass an index for n.
namespace details {
template<class T> struct tag{using type=T;};
template<class C, size_t n, unsigned start, unsigned width>
struct field {};
template<class Flags, class Field>
struct pseudo_ref {
Flags& flags;
template<class U>
constexpr pseudo_ref operator=( U&& u )const{
field_assign( flags, Field{}, std::forward<U>(u) );
return *this;
}
template<class T>
constexpr operator T()const{
return field_get( tag<T>{}, flags, Field{} );
}
};
template<class C, size_t n, class D, unsigned start, unsigned width>
constexpr auto operator*(
Flags<C,n,D>& flags, field<C, n, start, width>
)
-> psuedo_ref<Flags<C,n,D>, field<C,n,start,width>>
{
return {flags};
}
template<class C, size_t n, class D, unsigned start, unsigned width>
constexpr auto operator*(
Flags<C,n,D> const& flags, field<C,n,start,width>
)
-> psuedo_ref<Flags<C,n,D> const, field<C,n,start,width>>
{
return {flags};
}
template<class C, size_t n, class D, unsigned start, unsigned width, class U>
void field_assign( Flags<C,n,D>& flags, field<C,n,start,width>, U&& u ){
flags.set<start, width>(std::forward<U>(u));
}
template<class T,class C, size_t n, class D, unsigned start, unsigned width>
constexpr T field_get(
tag<T>, Flags<C,n,D> const& flags, field<C,n,start,width>
) {
return flags.get<start,width>();
}
template<class F>
struct field_end;
template<class C, size_t n, unsigned start, unsigned width>
struct field_end:std::integral_constant<unsigned, start+width>{};
}
template<class C, unsigned width, size_t n=0>
using first_field = field<C,n,0,width>;
template<class C, class F, unsigned width, size_t n=0>
using next_field = field<C,n,details::field_end<F>::value,width>;
now the syntax looks like this:
struct Test {
Flags<Test> flags;
};
first_field<Test, 1> one;
next_field<Test, decltype(one), 2> two;
Test t;
uint32_t o = t*one;
t*one = 1;
and everything is automatically dispatched to the bit fiddling code.
Note the complete lack of macros. You could use one to remove decltype above.
I know that overly complex and mind boggling C++11 and C++14 features are what's trendy, but here is a quick and dirty solution using evil macros:
#define GETMASK(index, size) (((1 << size) - 1) << index)
#define READFROM(data, index, size) ((data & GETMASK(index, size)) >> index)
#define WRITETO(data, index, size, value) (data = (data & (~GETMASK(index, size))) | (value << index))
#define FIELD(data, name, index, size) \
inline decltype(data) name() { return READFROM(data, index, size); } \
inline void set_##name(decltype(data) value) { WRITETO(data, index, size, value); }
And then simply:
struct Test {
uint flags;
FIELD(flags, one, 0, 1)
FIELD(flags, two, 1, 2)
};
t.set_two(3);
cout << t.two();
It generates accessors for the fields as if they are properties of the containing object, it is less verbose, and IMO also very readable, since that is the common way people expose object properties from encapsulated data.
Downsides - you have to calculate the field index yourself, but you can still use the approach with generating accessors together with your existing implementation relying on static fields with constructors to avoid it.
Upsides - it is short, simple, efficient and backward compatible - change the decltype to typeof and it will work with pre-c++11 and even plain C.