random
random

Reputation: 230

Integer template parameter for data size in class

I am writing a class calculating various metrics over an array of values. Initially, I had the list of the values initialized dynamically

class Foo
{
    int nStats;
    double * stats;
    Foo(int nStats);
    ~Foo();

    double F1();
    double F2();
    // ...
}

with the constructor simply initializing stats = new double[nStats];. However, for various reasons, I now need to have a fixed-size versions of the same class as well. The best way to achieve this seems by templating by size, in the style of std::array<>

template<int nStats>
class FooFixed
{
    double stats[nStats];

    double F1();
    double F2();
    // ...
}

I understand that even though the code for class functions F1, F2 ... is similar across all values of nStats, the nature of templates prevents me from defining them in source files, even in c++11. However, is there a trick which will allow me to code those functions only once, instead of copy-pasting them twice, first for initial class Foo and then for new templated class FooFixed<int>? As an example, summation function will clearly stay the same for both classes and I would hate to just copy-paste it twice.

Upvotes: 2

Views: 193

Answers (3)

Richard Hodges
Richard Hodges

Reputation: 69902

Standing on the shoulders of giants... refactoring Jared's code to eliminate the need for a specialisation, plus fixing some typos so it will compile gives us:

#include <vector>
#include <array>
#include <iostream>

template <class Container>
struct FooBase
{
    template<class...Args>
    FooBase(Args&&...args) : m_data { std::forward<Args>(args)... } {}

    using const_iterator = typename Container::value_type const *;

    const_iterator begin() const {
        return std::addressof(*std::begin(m_data));
    }

    const_iterator end() const {
        return std::next(begin(), m_data.size());
    }

protected:
    int* get_data() { m_data.data();}


private:
    Container m_data;
};

template <typename Container>
class Foo_impl : public FooBase<Container>
{
public:
    using FooBase<Container>::FooBase;


    double F1();
    double F2();
    // ...
};

using Foo = Foo_impl<std::vector<int>>;

template<std::size_t N>
using FooFixed = Foo_impl<std::array<int, N>>;

int main()
{

    auto fbv = Foo { 1, 2, 3, 4, 5 };
    auto fba = FooFixed<5> { 1, 2, 3, 4, 5 };

    auto fbv2 = Foo { std::begin(fba), std::end(fba) };


    return 0;
}

Upvotes: 0

W.F.
W.F.

Reputation: 13988

You could make use of static polymorphism mechanism:

template <class T>
struct FooParent {
   double *get_stats() {
      return static_cast<T*>(this)->stats;
   }
   int get_n() {
      return static_cast<T*>(this)->n;
   }
   // all your implementation using get_stats()[K] instead of stats[K]
};

struct Foo: FooParent<Foo> {
   int n;
   double * stats;
};

template <int nStats>
struct FooFixed: FooParent<FooFixed<nStats>> {
    int n = nStats;
    double stats[nStats];
};

Upvotes: 1

Jarod42
Jarod42

Reputation: 217810

You may have

template <typename Container>
class FooBase;

template <std::size_t N>
class FooBase<std::array<int, N>>
{
protected:
    FooBase() : m_data{} {}
    std::size_t get_size() const { return N; }
    int* get_data() { m_data.data();}
private:
    std::array<int, N> m_data;
};

template <>
class FooBase<std::vector<int>>
{
protected:
    explicit FooBase(std::size_t size) : m_data(size) {}
    std::size_t get_size() const { return m_data.size(); }
    int* get_data() { m_data.data();}
private:
    std::vector<int> m_data;
};

And then

template <typename Container>
class Foo_impl : protected FooBase<Container>
{
    using FooBase::Foobase;

    double F1();
    double F2();
    // ...
};

And finally:

template<std::size_t N>
using Foo = Foo_impl<std::vector<int>>;

template<std::size_t N>
using FooFixed = Foo_impl<std::array<int, N>>;

Upvotes: 2

Related Questions