开发者

Detect modification of variable at runtime in C/C++

开发者 https://www.devze.com 2022-12-22 14:06 出处:网络
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 initi

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 sn开发者_StackOverflow中文版ipplet, 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.


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.


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.


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.)


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.


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


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.


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


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

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


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?


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消