Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An equivalent of Objective-C's "valueForKey" methodology in C++?

Assume we have an Objective-C NSArray of instances of either the Shoe class or the Computer class. Both of these classes have the method age.

Objective-C would allow you to obtain the ages of all the shoes and computers in one simple statement like this:

NSArray *agesOfShoesAndComputers = [ourArray valueForKey:@"age"];

Does C++ have any similar methodology that can be applied to, say, vectors, or will stuff like this necessarily always be somewhat more cumbersome in C++?

like image 544
NoobOverflow Avatar asked Nov 23 '25 16:11

NoobOverflow


2 Answers

Firstly, std::vector<T> is statically typed. That is, you need to know whether it contains shoes, or computers, at compile time.

So, the short answer is that it'll always be more cumbersome in C++ (static typing is really an optimization, so this is the price we pay for performance).


If you want to use traditional runtime polymorphism, you need a common base class:

class MyBase
{
public:
    virtual ~MyBase();
    virtual int age() = 0;
};

class Shoe: public MyBase
{
public:
    virtual int age();
};
// same for Computer

now I can have

std::vector<std::unique_ptr<MyBase>> ourArray;

and

std::vector<int> ages(std::vector<std::unique_ptr<MyBase>> const &ourArray)
{
    std::vector<int> ages;
    std::transform(ourArray.begin(), ourArray.end(),
                   std::back_inserter<int>(ages),
                   [](std::unique_ptr<MyBase> const& elem) { return elem->age(); }
                  );
    return ages;
}

The call to elem->age() here relies on the common interface defined by MyBase, but dispatches to the derived class implementations (Shoe::age() and Computer::age() respectively).

It's less flexible than the Objective C version, because your common interface, inheritance etc. are all still statically defined.

Note that if this is a simple property, you could just skip the virtual method call entirely and put it in MyBase instead. Shoes and Computers will still need to inherit from this class, but they can just set the property value in the base class.


You may be able to use compile-time polymorphism instead, ie, templates. This is in some ways more similar to the Objective C approach, because it works for any type that supports the expected method, irrespective of inheritance relationships.

template <typename T> // T could be Shoe, Computer or anything else
std::vector<int> ages(std::vector<T> const &ourArray)
{
    std::vector<int> ages;
    std::transform(ourArray.begin(), ourArray.end(),
                   std::back_inserter<int>(ages),
                   [](T const& elem) { return elem.age(); }
                  );
    return ages;
}

the mechanism is completely different though - you still have statically distinct call sites with either vector<Shoe> or vector<Computer> or whatever, and never the twain shall meet.

like image 146
Useless Avatar answered Nov 25 '25 04:11

Useless


C++ is somewhat less dynamic, but what you're looking for is generally transform:

vector<unique_ptr<BaseClassForShoeAndComputer>> ourArray;
vector<int> agesOfShoesAndComputers;

transform(ourArray.begin(), ourArray.end(),
          back_inserter(agesOfShoesAndComputers),
          [](unique_ptr<BaseClassForShoeAndComputer>& s) { return s->age(); });

However, you can't really have unrelated objects inside a single container (well, you can, but it's another thing to use them); you can however, use templates (unfortunately, not with lambdas) to achieve static duck typing (for use with any type with an "age" method).

like image 25
eq- Avatar answered Nov 25 '25 06:11

eq-



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!