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 initializem_distanceAttenuation
andDistanceAttenuation
is part ofFoo
, 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 initializem_distanceAttenuation
; something like2) in C++14, a
constexpr
method isn't aconst
method; suggestion: define the following method asconst
too or you can't use they in someconstexpr
expressions3) in
attenuateBy()
, the test in the followingfor
is ever truebecause a
std::size_t
is ever>= 0
, so thefor
goes in loop and never exit; suggestion: redefinei
asint
orlong
4) in
attenuateFoo()
you usem_DistanceAttenuation
where the variable is defined asm_distanceAttenuation
; suggestion: correct che name of the variable used5) in
attenuateFoo()
you call the methodattenuateSignalByDistance()
using the::
operator; suggestion: use the.
operator, so (considering point (4) too)