3

So I have a pure virtual class that needs to stay that way. In this class I have or technically need a method that takes in a templatized parameter. Here is the object type for the parameter:

template <int LENGTH>
struct MyStruct
{
  int arr[LENGTH];
};

And here is my method:

template <int LENGTH>
virtual bool send_struct(const MyStruct<LENGTH>& mystruct) = 0;

However, obviously I cannot use a template with a virtual class, the alternative being adding the template to the class, which I also cannot do for my purpose. Is there an alternative to this while keeping my constraints? I need to pass array of different sizes to this method inside my pure virtual class, but I cannot templatize the class.

arias_JC
  • 549
  • 3
  • 15

3 Answers3

2

You can't write this, as you know:

template <int LENGTH>
virtual bool send_struct(const MyStruct<LENGTH>& mystruct) = 0;

There is no way to pass through a compile-time LENGTH. But you can pass through a run-time length by type-erasing your container:

virtual bool send_struct(gsl::span<int const> ) = 0;

gsl::span<T> is a non-owning, contiguous container of Ts. It's a view onto an array or a vector or whatever else, which can be a MyStruct<N> too. This type isn't directly constructible from your struct, but it's easy to write a version that is, or to add the necessary members to yours (.data() and .size()) to make it work.

An extremely simple implementation would just be:

class my_span {
private:
    int const* begin_;
    int const* end_;

public:
    template <int L>
    my_span(MyStruct<L> const& ms)
        : begin_(ms.arr)
        , end_(ms.arr + L)
    { }

    int const* begin() const { return begin_; }
    int const* end() const { return end_; }
    int const* data() const { return begin_; }
    size_t size() const { return end_ - begin_; }
};

And now you have a easily overridable virtual function for some non-modifiable container of ints.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • would my function then be: `virtual bool send_struct(my_span &ms) = 0;` or something? – arias_JC Feb 02 '17 at 13:44
  • @arias_JC Not by reference, by value. – Barry Feb 02 '17 at 14:26
  • In your my_span class for `end_`, would it not be `end_(&ms.arr[L-1])` because as it is `my_span::end()` returns 0. – arias_JC Feb 02 '17 at 16:18
  • 1
    @arias_JC No it shouldn't be, that'd remove the last element from the range. – Barry Feb 02 '17 at 17:11
  • I guess what I am asking is in your `int const* end() const { return end_; }` function, what is it suppose to be returning. Because it has a pointer at the size of the array which would not be the last element but the one after, therefore always returning zero or technically null. – arias_JC Feb 03 '17 at 13:16
  • @arias_JC Yes, it returns a pointer to one past the end element. That's how ranges in C++ work. It does not always return zero - or really, ever return zero. – Barry Feb 03 '17 at 13:24
0

Templates and virtual methods do not get along well. You will need to first isolate the templated portion of MyStruct from the rest of the class and only deal with the non-template portion with virtual methods. Second, you need to abstract the templated portion to provide a non-template interface that the virtual methods can use. For arrays, this is easy. You can simply provide a pointer and a size. To translate between the representations, add an intermediate non-virtual templated method which calls the actual non-template virtual method.

struct MyStruct 
{
    // All non-templated members
};

template <int LENGTH>
struct MyStructArray : public MyStruct
{
    // All templated members
    int arr[LENGTH];
};


struct OtherStruct
{
    virtual bool send_struct_impl(const MyStruct& mystruct, const int * arr, int length) = 0;

    template <int LENGTH>
    bool send_struct(const MyStructArray<LENGTH>& mystruct)
    {
        return send_struct_impl(mystruct, mystruct.arr, LENGTH);
    }
};

Alternatively, if you don't mind the overhead, you can store a pointer and size for MyStructArray::arr in MyStruct. This would allow you to use arr in every context where a non-template reference or pointer to MyStruct is used.

struct MyStruct 
{
protected:
    MyStruct(int * p_arr_ptr, int p_arr_length) :
        arr_ptr(p_arr_ptr), arr_length(p_arr_length) {}

public:
    int * arr_ptr;
    int arr_length;

    // All other members
};

template <int LENGTH>
struct MyStructArray : public MyStruct
{
    MyStructArray() : MyStruct(arr, LENGTH) {}
    int arr[LENGTH];
};
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
-1

You could make MyStruct<int> types inherit an empty base class:

struct BaseStruct {};

template <int LENGTH>
struct MyStruct : BaseStruct 
{
  int arr[LENGTH];
};

and define a virtual function which takes a BaseStruct and a length:

virtual bool send_struct(const BaseStruct& mystruct, int length) = 0;

Eventually, you could provide a convenient function to convert from those BaseStruct and length to a MyStruct<length>:

template <int LENGTH>
const MyStruct<LENGTH>& to_mystruct(const BaseStruct& mystruct)
{
    return static_cast<const MyStruct<LENGTH>&>(mystruct);
}

Demo on coliru

YSC
  • 38,212
  • 9
  • 96
  • 149
  • How do you determine what `LENGTH` should be? – NathanOliver Feb 01 '17 at 17:24
  • @NathanOliver Obviously, it needs to be known at compile time by the class implementing `send_struct`. See added demo. – YSC Feb 01 '17 at 17:31
  • 1
    No sure that that helps. Since this is a member function, if they need to cast it back to the actual `MyStruct<>` then they can't get the size. – NathanOliver Feb 01 '17 at 17:34
  • @Nathan I'd use something like a free function taking a good ole c-style opaque pointer. – πάντα ῥεῖ Feb 01 '17 at 17:36
  • @NathanOliver It depends on what OP needs. But just to clarify, when you write "_Since this is a member function_", what function are you talking about? – YSC Feb 01 '17 at 17:46
  • `send_struct` is a member function. I'm asking how it should convert the `MyBase` to the `MyStruct` when it does not know its size. Hard coding the size into the class does not seem right since you just made the function able to take any `MyStruct`. If your going to have a hard coded value in the class then whats the point of taking a `MyBase` when you could just hard code the `MyStruct`? – NathanOliver Feb 01 '17 at 17:49
  • @NathanOliver Since a `MyStruct` size is known at compile-time, I'm guessing a class handling that kind of object may know, at compile-time, this size. If it does, my suggestion fix the "`send_struct` _needs to be a virtual function_" request from OP. If it does not, they might prefer a dynamically sized vector, don't they? Anyway, I'm not sure either this is the way; it's clearly _one_ way. – YSC Feb 01 '17 at 17:52
  • @YSC Looks good but my purpose is, when passing an array to that function, the array already has data. The derived classes implementing the method are not going to know the size, but the actual array passed in already contains all that information – arias_JC Feb 01 '17 at 19:52
  • 1
    @arias_JC If the derived classes implementing the method do not know the size then `MyStruct` is not fit for purpose. `MyStruct` by definition can only be used by functions that know (at compile time) the size. – Chris Drew Feb 02 '17 at 08:43
  • @ChrisDrew Not if you have separate classes implementing `MyStruct` and them giving it its size. Then those class types can call to the derived classes implementing the method, therefore the derived classes do not need the size. – arias_JC Feb 02 '17 at 12:49
  • 1
    Guys, I understand the solution I suggested may not be the best in all situation, but with the question as it's stated, it's still is _a_ solution. Why the downvotes? – YSC Feb 02 '17 at 13:09