views:

157

answers:

1

Entity has a member var of type std::array. Student inherits from Entity, and will need to initialize the std::array member var it inherited. Below is the code I'm using to do this, but it involves casting a brace-enclosed list to std::array. I'm not sure this is the correct or optimal way to do this. Using a brace-enclosed or double brace-enclosed list without the cast results in compilation errors. I've tried several other ways of initializing the std::array member var with no success, so I seem to be stuck with my current method. Is there a better way to do this?:

template<typename... Args> struct Entity {
    typedef const char* name_t;
    typedef const array<const char*, sizeof...(Args)> source_names_t;

    const tuple<Args...> data;
    name_t name;

    //This will be initialized by derived class Student.
    source_names_t source_names;

    Entity(
       name_t tmp_name
       , source_names_t tmp_source_names
    )
        : name(tmp_name)
        , source_names(tmp_source_names)
    {}
};

//idnum, fname, lname, married
struct Student : Entity<int, string, string, bool> {

    Student()
        : Student::Entity(
            "student"

            //Now Student initializes the array, but does it by casting.
            , (source_names_t) {{"id", "lname", "fname", "married"}}
        )
    {}
};
+1  A: 

There are two options, but one relies on runtime size verification. Note the latter in my example is equivalent to a cast. What is wrong with casting?

#include <cassert>
#include <algorithm>
#include <array>
#include <initializer_list>
#include <iostream>

struct A {
  typedef std::array<char const*, 3> T;
  T data_;

  A(std::initializer_list<char const*> const& data) {
    assert(data.size() <= data_.size());  // or ==
    // of course, use different error handling as appropriate
    std::copy(data.begin(), data.end(), data_.begin());
    std::fill(data_.begin() + data.size(), data_.end(), nullptr);
  }

  A(T const& data) : data_ (data) {}
};

int main() {
  A a ({"a", "b"});
  std::cout << (void const*)a.data_[2] << '\n';

  A b ((A::T{"a", "b"}));  // might just be the compiler I tested, but the
                           // extra parens were required; could be related
                           // to "the most vexing parse" problem
  std::cout << (void const*)b.data_[2] << '\n';

  return 0;
}

However, it looks like this data is identical for each Student object. Why not either use a virtual method or pass a shared object to the base ctor? You can either copy that object, entity_data_ below—which is equivalent to your current code—or require that it outlive and store a pointer/reference.

struct Student : Entity<int, string, string, bool> {
  Student() : Entity<int, string, string, bool>("student", entity_data_) {}
  // the template arguments are still required in 0x, I believe
private:
  static Entity<int, string, string, bool>::source_names_t entity_data_;
}
Roger Pate
What's wrong with casting? Well... nothing I guess, I just didn't know if I was missing something important, or if there was a better way perhaps. It just seemed odd to me that I'd have to cast a brace-list to a std::array object in order to initialize it in the initializer list. Doing that with a raw array I figured would be fine, but this is my first time messing w/ std::array vs raw arrays. Not quite sure what no-nos to avoid. Anyway, thanks. If no ones sees a problem with that cast given the scenario I painted, then I guess casting is the way to go.
pheadbaq