Home > OS >  how call a method once per sequence?
how call a method once per sequence?

Time:02-24

Given a code below

class Base {
public:
    virtual void callThisOnce();
};

class Derived_A: public Base {};
class Derived_B: public Base {};

void function(std::vector<Base> v) {
   for (auto i : v)
       i.callThisOnce();
}

Vector v contains either or both Derived_A and Derived_B objects.
What I want to archive is that when ever function is called, if in v there is a object belong to class Derived_A, Derived_A::callThisOnce should be executed once; and if there is a object belong to class Derived_B, Derived_B::callThisOnce should be executed once.
I need a coding pattern that make it easiest to create Derived_C.
I tried this

class Derived_A: public Base {
    virtual void callThisOnce(){
        if(!_mutex.try_lock())
             return;
        /* Do something */
    }
    static std::mutex _mutex;
};

void function(std::vector<Base> v) {
   for (auto i : v)
       i.callThisOnce();
   Derived_A::_mutex.try_lock(); // this call prevent undefined behavior of mutex::unlock
   Derived_A::_mutex.unlock();
}

This pattern make me create a static mutex an required me to call std::mutex::unlock of all class. But it's seem to leave many problems.

Is there a better method to archive the same?

CodePudding user response:

std::vector<Base> v

vector v contains either or both Derived_A and Derived_B objects.

What you describe isn't possible. A vector of Base can only contain objects of type Base and not objects of type Derived_A or Derived_B.

If you want polymorphic storage, then you need indirection. And if you want to combine indirection with the lifetime of the vector, then you need smart pointers. And if you want unique ownership, then you need a virtual destructor:

struct Base {
    virtual void callThisOnce();
    virtual ~Base() = default;
};

struct Derived_A: Base {};
struct Derived_B: Base {};

void function(std::vector<std::unique_ptr<Base>>& v);

To call a member function once per unique dynamic type, you could store each encountered std::type_index in a set and call the function only for the first one:

std::unordered_set<std::type_index> types;
for (auto&& ptr : v) {
   auto [it, first] = types.emplace(typeid(*ptr));
   if (first) {
       ptr->callThisOnce();
   }

CodePudding user response:

If I got your intention right one way to achieve this is by combining runtime polymorphism (the correct function gets called) with using a static variable in each class (to track down if function is called only once per class and not per object):

class Base {
public:
    virtual void callThisOnce() {
        if (!base_called) {
            std::cout << "Base::callThisOnce" << std::endl;
            base_called = true;
        }
    };

    static bool base_called;
};

class Derived_A: public Base {
public:
    virtual void callThisOnce() override {
        if (!deriveda_called) {
            std::cout << "Derived_A::callThisOnce" << std::endl;
            deriveda_called = true;
        }
    }

    static bool deriveda_called;
};


class Derived_B: public Base {
public:
    virtual void callThisOnce() override {
        if (!derivedb_called) {
            std::cout << "Derived_B::callThisOnce" << std::endl;
            derivedb_called = true;
        }
    }

    static bool derivedb_called;
};


bool Base::base_called;
bool Derived_A::deriveda_called;
bool Derived_B::derivedb_called;

void function(const std::vector<Base*> &v) {
   Base::base_called = false;
   Derived_A::deriveda_called = false;
   Derived_B::derivedb_called = false;

   for (auto i : v)
       i->callThisOnce();
}

int main() {
    std::vector<Base*> v;
    Base b1, b2;
    Derived_A da1, da2;
    Derived_B db1, db2;
    
    v.push_back(&b1);
    v.push_back(&b2);
    v.push_back(&da1);
    v.push_back(&da2);
    v.push_back(&db1);
    v.push_back(&db2);
    
    function(v);

    return 0;
}

which will output:

Base::callThisOnce
Derived_A::callThisOnce
Derived_B::callThisOnce

EDIT:

The static variables should get reset after the callThisOnce calls to avoid some unwanted behaviour with recursive calls of function (thanks to @Jarod42)

void function(const std::vector<Base*> &v) {
   for (auto i : v)
       i->callThisOnce();
   
   Base::base_called = false;
   Derived_A::deriveda_called = false;
   Derived_B::derivedb_called = false;
}
  •  Tags:  
  • c
  • Related