3

Not sure if this can be done using templates but I want to give it a try.

I have a template class which takes any struct, stores it and returns it. Additionally, I want an interface that resets the struct's data whenever requested.

#define MYDEFAULT {1,2,3}

template < typename ITEM, ITEM Default>
class myClass{
public:
    myClass(ITEM item) : _item(item) {}

    const ITEM* get(){
        return &_item;
    }

    void reset(){
        _item = Default;
    }

    ITEM _item;
};
// Set to default when instantiated
myClass<myStruct, MYDEFAULT> ABC(MYDEFAULT);

Of course that's not working at all, but what I want to achieve is the replacement of Default in reset(). I mean it would work if _item would be of type int.
How can this be realized?

EDIT: I want something like this:

template <typename Y, Y T>
class myclass {
public:
void reset() {
    xxx = T;
}
    Y xxx{10};
};


void test()
{
    myclass<int, 5> _myclass;
}

Initially xxx is 10 and after invoking reset it is 5. This works, so it seems it is not possible for POD or class types?

EDIT2: It seems it is all about non-type template-arguments. https://stackoverflow.com/a/2183121/221226 So there is no way around traits when using structs.

Community
  • 1
  • 1
binaryguy
  • 1,167
  • 3
  • 12
  • 29
  • I'm not sure I got what you want exactly. Anyway, I've added one more answer that maybe fits your needs. Let me know if it goes along the right direction at least... :-) – skypjack Aug 16 '16 at 21:40

2 Answers2

2

As a viable solution, you can use a trait class as shown in the following working example:

#include<cassert>

struct S {
    int i;
};

template<typename T>
struct Traits {
    static constexpr auto def() { return T{}; }
};

template<>
struct Traits<S> {
    static constexpr auto def() { return S{42}; }
};

template <typename ITEM>
class myClass {
public:
    myClass(): _item(Traits<ITEM>::def()) {}
    myClass(ITEM item): _item(item) {}

    const ITEM* get() {
        return &_item;
    }

    void reset() {
        _item = Traits<ITEM>::def();
    }

    ITEM _item;
};

int main() {
    myClass<S> ABC{};
    myClass<int> is;
    assert((ABC.get()->i == 42));
    assert((*is.get() == 0));
}

The basic trait uses the default constructor of the type ITEM.
You can then specialize it whenever you want a different defaulted value for a specific class.

The same can be accomplished even with a factory function as:

template<typename T>
constexpr auto def() { return T{}; }

template<>
constexpr auto def<S>() { return S{42}; }

Anyway, traits can easily provide more types and functions all at once.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • This works nicely, thank you so far. But I am looking for a way where I would not need to define another trait template. I'd be nice to pass the struct somehow to myClass directly; leveraging somehow the template capabilities. Or is there really no way around it? I mean it is just a replacement isn't it? – binaryguy Aug 16 '16 at 13:39
  • @wanderameise I'm adding another answer. Let me know if it works for you. I'd leave this one untouched for still I find it a valid alternative. – skypjack Aug 16 '16 at 21:05
  • Yes, it is a total valid alternative that has proofen to work nicely! – binaryguy Aug 17 '16 at 08:00
0

You can maybe simulate it using a data structure with a data member of type std::array.
A minimal, working example follows:

#include<cstddef>
#include<array>
#include<cassert>

template<typename T, T... I>
struct S {
    S(): arr{ I... } {}

    S(const T (&val)[sizeof...(I)]) {
        for(std::size_t i = 0; i < sizeof...(I); ++i) {
            arr[i] = val[i];
        }
    }

    const T * get() {
        return arr.data();
    }

    void reset() {
        arr = { I... };
    }

private:
    std::array<T, sizeof...(I)> arr;
};

int main() {
    S<int, 1, 3, 5> s{{ 0, 1, 2 }};
    assert(s.get()[1] == 1);
    s.reset();
    assert(s.get()[1] == 3);
}

I'm not sure I got exactly what you are asking for, but the interface in the example is close to the one in the question and the implementation details should not affect the users of your class.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • ok, but then I would have to store the defaults in an array which is not what I want since this is different to the solution I posted in the first edit where no extra space is occupied. But hey, thanks anyway I learned a lot about templates. I guess there is just no simple solution for struct types... – binaryguy Aug 17 '16 at 09:52
  • @wanderameise No, defaults (`I...`) are not stored anywhere, they are a parameter pack that is part of the class once instantiated. As you can see from the example above, you can freely use a different set of values to initialize the class, then call reset and find that the defaults have been put in your array. – skypjack Aug 17 '16 at 10:34
  • But I would have to map all values to its indices here: S s{{ 0, 1, 2 }}; ? Also, T is an integral type but I need will need to maintain a struct. – binaryguy Aug 17 '16 at 13:09
  • @wanderameise No, sorry, it wasn't that the intention. You can freely use `S s{{ 3, 10, 42 }};`. They are the initial values to be used, that's all. Of course, if you change them in the example, you must update the `assert`s in the `main` accordingly. – skypjack Aug 17 '16 at 13:13
  • Instead of an array could you demonstrate this using a struct as member? and when you initialize S can you pass the struct defaults: {1,2,3,4,5}? – binaryguy Aug 17 '16 at 13:22