tags:

views:

297

answers:

10

Hello,

I am developing a library in C++ where users/programmer will extend a class BaseClass that has a method initArray. This method should be implemented by the user/programmer and it should normally initialize all elements of the array m_arr.

Here is a snipplet, modified to this example:

class BaseClass {
     public:

     BaseClass(int n) {
         m_arr = new double[n]; 
         size = n;
     };
     virtual ~BaseClass();

     int size;
     double* m_arr;

     virtual int initArray();
};

Sometimes, the user/programmer implements a initArray that does not initialize some elements of m_arr. What I would like is to create a function in my library that checks if initArray did initialize all elements of m_arr. This function should be called by a sanity-check rutine at runtime.

My question: is it possible to detect changes on this array? I can only think of initializing the array with some invalid values (like NaN or Inf), call initArray and check that all values have changed.

Thanks for your ideas,

David


Edit

Here is an example of the client code that I am trying to detect:

// .h:
class MyExample : public BaseClass {
     public:
     MyExample();
     virtual ~MyExample();
     virtual int initArray();
};

// .cpp:
MyExample::MyExample() : BaseClass(3) { }
MyExample::~MyExample() { }
int MyExample::initArray() {
     m_arr[0] = 10;
     //m_arr[1] = 11; // let's say someone forgot this line
     m_arr[2] = 12;
     return 0;
}

So, by forgetting the m_arr[1] this element is not initialized and could cause problems in future calculations. That's what I would like to check.

+5  A: 

Why not use a std::vector? The end-user would then add to it using push_back, and you can check the size to see how many items were added.

anon
Because I am maintaining this code and it uses the array structure a lot to couple the library with some libraries written in C.
YuppieNetworking
@YuppieNetworking You can use vectors with C code.
anon
@Neil Butterworth Note that std::vector is reallocated on some of the size changes and the memory locations of elements may be changed as well. This is sometimes critical.
doc
@Neil Butterworth: Yes, I agree, and probably std::vector is the way to go for future versiones. I could change the code to use vector, and double-check the coupling with C libraries that use double*. However the problem is that there is already a number of users/programmers that code initArray this way, as a double*, and I was hoping to make the impact on the users/programmers as small as possible.
YuppieNetworking
In your existing code you know the size up front: Why not reserve that much space then, and use the vector idea? Edit: Can't spell.
Mark B
@Mark B because then you could not find if array was initialized.
doc
@Mark B: +1 to doc comment, I will find myself with the same problem
YuppieNetworking
Note: reserve NOT resize. The reserve call just ensures that it won't get resized and moved while the client uses push_back to add their elements as Neil proposed initially. Then you ask for the size (which is not the same thing as the reserved space) to determine if all elements were initialized.
Mark B
@Mark B Someone may forget to initialize values just like when using plain array. Vector is not a list and it provides random access, thus you can do `vector[0] = 1.0; vector[30] = 0.0` without middle steps. To make this sense, you have to hide vector over your own structure. Also reserve() may allocate more space than required. What for? std::vector just doesn't fit into some problems and this is one of them. Arrays are not evil as some people are trying to persuade.
doc
As I understand Neil's solution, there would be no possibility to forget initialization. 1) array becomes vector member. 2) Base constructor calls reserve *but doesn't add any elements*. 3) Child class initializes the array *by using push_back*. 4) Base class compares the expected length of the array with the vec.size() length to see if they match and verify that elements have been added. If the child class did push_back with bogus data, nothing you can do.
Mark B
@Mark B how are you going to prevent subclasses from use of subscript operators []? If you assume that subclass is doing everything right, then I don't sea a reason why check at all if array was initialized properly.
doc
Point, they can still invoke undefined behavior using [] on elements they didn't push_back yet. It just makes it a bit harder to break things.
Mark B
They can use `[]` on array members they did not initialized yet... there is nothing new there.
Matthieu M.
+1  A: 

Your idea of using inf or NaN would work. Any predetermined initializer would work well enough.

Another idea is to create an accessor member funtion to modify elements. In the accessor you would set the modified flag, in the InitArray() you would clear the modified flag.

terry
Idea wit NaNs will work until subclass isn't initializing array with NaNs by itself. Unfortunately this may be a part of subclass' functionality. Just imagine that subclass takes two parameters `a, b` and the initial value is `a/b`. Resulting NaN (eg in case of a=0 and b=0) is a part of its specification. Or, on the other side, may the initialization be so complex that it unintentionally can return NaN due to round errors. Possible errors which may occur with such approach may be very hard to track.
doc
@doc: great point
YuppieNetworking
A: 

This depends on what you are trying to do: do you actually need to create the array in the base class, or can you use Neil's suggestion of push_back()?

Anyway, consider using a std::vector<> (why do your own memory management?) of boost::optional<double>. (You do know about the boost libraries?)

Also, do you really want to split the object creation into two phases? Any client code that creates a BaseClass descendant needs to call initArray(). Are you concerned about efficiency?

Pontus Gagge
This design is because the BaseClass is actually an object with states (like in a simulation). initArray initializes the state of the object and it could be initialized several times as the states change.
YuppieNetworking
`boost::optional` is great but cannot be exposed safely to C-code.
Matthieu M.
+2  A: 

Here is my suggestion:

Add another protected virtual method to the base-class which called "InitArrayImpl" and order the creator of the subclass to fill the array in it.

The initArray method should be public non-virtul , in this method , make a call to InitArrayImpl , before the call , initialize the array with invalid values , after the call , test if all values were changed.

The client of the class should be exposed only to the initArray method.

void Base::initArray() { initArrayWithInvalidValue(); initArrayImpl(); checkArray(); }where initArrayImpl() is a virtual function.
Corwin
Yes, my initial thoughts were on this direction. That's why I asked you guys, since it could probably be done in some other way.
YuppieNetworking
That's the idiomatic way. Indeed if you listen to Herb Sutter's advice (and it's like he knows anything right ?) interfaces should never expose virtual methods. non-virtual methods invoking private virtual methods allow to check for pre- and post-conditions in the base class.
Matthieu M.
+3  A: 

If you're trying to make a "foolproof" interface, rather than thinking of initArray() as a routine with side effects...why not stylize it as something more functional? initArray() could have the responsibility of allocating and constructing a vector, and pass back a reference to that fully constructed object. This would also allow you to make m_arr private:

class BaseClass {
private:
    size_t size;
    auto_ptr< vector< double > > m_arr;

public:
    BaseClass(size_t n) {
        size = n;
     };
     virtual ~BaseClass();

protected:
     virtual auto_ptr< vector< double > > initArray() const;
};

(Note: After you call initArray you can verify that the vector the derived class passes back is the right size. Or if you don't need to enforce the size up front, you can just nix that parameter from the constructor and accept whatever length initArray returns as the intended size.)

Hostile Fork
I agree with your functional approach. I think I'll head this way since I like the routine without side effects.However, as I told Neil, I do have a lot of users/programmers that already code with the initArray and its side effects, so I was hoping to improve some details about this library with a low impact on their habits.
YuppieNetworking
A: 

I think you are whistling in the wind on this. You cannot dictate that all the values are properly filled in. At best all you can do is dictate that the user stuffs some value into the array elements. If you stuff NaNs into the array which you then sanity check, all your users will do is initialize with zero, -1 or MAX_DOUBLE. So you might just as well provide them with a ctor to do that and be done with it.

My derived class may well use the base class to reserve 1000 elements but it may contain its own count of how many elements it has actually used.

lilburne
+1  A: 

If efficiency isn't one of your main objectives, you may

  • Block direct access to m_arr by making it private
  • add array of bools of the same size as m_arr m_field_initialized = new bool[n] and initialize it with false values.
  • Create public (or protected) instance of accessor class that will wrap m_arr and m_field_initialized
  • Accessor should have setVal(int i, double val) method which will set m_arr[i] and additionaly m_field_initialized[i] flag to true;
  • In your initialization check method test if all fields of m_field_initialized were set to true.

You may improve this by providing method for faster direct access, when initialization conditions were met. It shall return null pointer before initialization check, and after initialization was successfull it would return your array pointer.

double * directAccess() {
   if (m_arr_initialized) return m_arr;
   else return 0;
}

m_arr_initialized shall be set by your initialization check method.


If it is not required for the array to be allocated in the base class you may set m_arr to zero, leave allocation for subclasses and just check if m_arr pointer was set to non-zero. Additionaly valid field denoting allocated size may be set. Or you can like previously block access to m_arr and provide method in base class for allocation allocate(std::size_t size), or allocation with initial value allocate(std::size_t size, double initVal) or even enforce initializing function to be passed allocate(std::size_t size, double (*callback)(std::size_t element)), which will be called on each element. There are many possibilities.


edit: after your edit I suggest a pointer (or reference) to either initialization object or callback to a function in BaseClass constructor. This will enforce subclasses to provide initialization code. Consider the following:

class InitializerInterface
{
   public:
     virtual double get(int element) const = 0; 
}

In your base class constructor

 BaseClass(int n, const InitializerInterface & initializer) {
     m_arr = new double[n]; 
     size = n;
     for (int i = 0; i < n; i++)
        m_arr[i] = initializer.get(i);
 };

Now any subclass must pass some initialization to constructor. You can of course replace get() method with a functor or add support for callback function as well. It depends on your preferences.

//last edit to make it const-correct

doc
+1  A: 

This looks less like C++ and more like C with a couple helpful features like constructors to me. Your class can't make up its mind if it's a C-struct or a C++-class and I think that's part of the problem you're having with initialization here.

What about doing this a different way, making the array private and providing a non-virtual initialize interface that accepts a functor kind of like std::generate. You then call the functor once for each element. That way you know whether or not they called your initializer and you know that all the elements are safely initialized. Not only that, they're then protected from child classes changing them around whenever they like.

If you're required to keep your current approach for some reason or other, using NaN or inf might work if you can guarantee that those values won't trap an exception on any hardware you plan to release the library for. More safely, just pick some neutral value such as 0 or one and if the client fails to initialize, just make it perfectly clear that they're shooting themselves in their foot.

In other words having that data be public AND enforcing that it gets initialized sanely are two (almost) mutually exclusive goals.

Mark B
It looks like C + couple of features because the code is heavily trimmed. I agree with your NaN point.
YuppieNetworking
+2  A: 

I don't see a straightforward way of doing this in C++. What you are intending to implement is filters in Ruby on Rails where before accessing any method, the filters are invoked.

Alternatively you can wrap your array inside a structure and inside this structure overload the [] operator for both assignment and access.

Now :

1) Inside the overloaded [] operator for assignment, maintain a counter and increment the counter for each initialization.

2) Inside the overloaded [] access operator, check the counter before you access the array contents if it is equal to total number of elements. If not, throw error.

Hope this helps.

hype
I like this answer because it stays with the structure proposed by the example.
YuppieNetworking
m_arr[0] = 10; m_arr[0] = 11; // Oops, copy-paste bug! m_arr[2] = 12;
Mark B
@Mark thanks for pointing that out. You can add a condition before incrementing the counter to check if the current element is not already initialized
hype
Yeah that should do the trick.
Mark B
It's much more difficult than that: `m_arr[0] = 10; if (m_c) m_arr[0] = 11;` would mess up the counter... it's MUCH easier to check the array afterward, and it does not incur an overhead at each `m_arr[]` call either, because once you're past the initialization phase it's pretty useless...
Matthieu M.
A: 

You can also set a data break point via debug registers. This link has some infos on the topic.

smocoder
Thank you but I am looking for a runtime solution, without debuggers.
YuppieNetworking