Simple constexpr LookUpTable in C++14

2019-04-23 02:51发布

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.

1条回答
放荡不羁爱自由
2楼-- · 2019-04-23 03:21

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);}
查看更多
登录 后发表回答