Help with downcasting

Hi, I have some C++ code and I'm not sure how to properly downcast a class. I have a generic structure (called Base) and a bunch of derived classes (100+ of them) that do various things to the members of the Base class. The trouble is users of this library can create new derived classes and "register" them so their callback will be called. Ideally, their callback will be of the right type. Derived classes do not and can not have any additional members, only functions that manipulate the state of the Base class.

In the real code, I don't know for sure which (if any) derived classes can handle the data in the Base class.

Here's some sample code I have and some questions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>

class Base {
	public:
	char buf[12];
	virtual bool isOneOfThese(Base &b) { return false; };
};

class Derived1 : public Base {
	public:
	Derived1(Base &b) : Base(b) {}
	int getSum(void) {
		int sum = 0;
		for (int i = 0; i < sizeof(buf); i++) {
			sum += buf[i];
		}
		return sum;
	}
	static bool staticIsOneOfThese(Base &b) {
		return b.buf[0] == 1;
	}
	bool isOneOfThese() {
		return buf[0] == 1;
	}
};

// ... more derived classes here
//

void func(Derived1 &d) {
	std::cout << d.getSum() << std::endl;
}

int main(int argc, char *argv[]) {
        // manually build a Base for testing; these come from somewhere else
	Base b;
	for (auto i = 0; i < sizeof(b.buf); i++) {
		b.buf[i] = 1;
	}

	if (Derived1::staticIsOneOfThese(b)) {
		func(static_cast<Derived1&>(b));
	}
	return 0;
}


Questions:

(1) Is it safe to use static_cast here? I just want to call some methods in Derived1 at this point of the code.
(2) How can I code this to call isOneOfThese instead of staticIsOneOfThese. Another cast?
(3) Should I instead be using a visitor pattern?
(4) I'd like to have a generic "register" function that takes the Derived class and then the Base class can call it if it meets the isOneOfThese criteria.

Any suggestions?
Last edited on
It seems to me like this is a misuse of inheritance. If none of the derived classes add any state, then why not simply have all the member functions of the derived classes in the base class? What are you gaining from this design?
Do you mean something like this - which doesn't use any casting?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstring>

class Base {
public:
	Base() {}
	Base(const char* b) {
		strcpy(buf, b);
	}

	virtual int getSum() { return 0; }
	virtual bool isOneOfThese() const { return false; }

public:
	char buf[12] {};
};

class Derived1 : public Base {
public:
	Derived1(const Base& b) : Base(b) {}

	int getSum(void) override {
		int sum = 0;

		for (int i = 0; i < sizeof(buf); i++) {
			sum += buf[i];
		}

		return sum;
	}

	bool isOneOfThese() const override {
		return buf[0] == 1;
	}
};

// ... more derived classes here
//

void func(Base& b) {
	std::cout << b.getSum() << std::endl;
}

int main(int argc, char* argv[]) {
	// manually build a Base for testing; these come from somewhere else
	Base b;

	for (auto i = 0; i < sizeof(b.buf); i++) {
		b.buf[i] = 1;
	}

	Derived1 d(b);

	if (d.isOneOfThese()) {
		func(d);
	}

	return 0;
}


which displays:


12

Last edited on
I want someone to be able to define a class that has additional methods that can be called against the base object. The data in the Base class "defines" which type of object this really is, and the code may or may not have a valid subclass for this type.

seeplus, while your code works, the snag here is that I'd prefer not to create an additional object, instead just use the current object to avoid a memory copy (performance matters here).

I don't know all the Derived class instances; the base class is in a library and users will be extending it to add their own functionality.
If you are downcasting you shouldn't use static_cast, it can lead to undefined behavior if the cast can't be done. You use dynamic_cast. And that will have performance costs.

https://stackoverflow.com/questions/6322949/downcasting-using-the-static-cast-in-c
I think the problem here is that the type OP wants to apply is dependent on the state of the object, which is independent of its type. Like, there's nothing preventing Derived1::staticIsOneOfThese(b) from returning different values for the same object b at different points in the execution. Like I said, I really think inheritance is the wrong tool for this job.

OP, what exactly are you trying to do? What do you use this for?
Last edited on
@helios, I agree, inheritance is not the best tool in this instance. But the OP seems determined to use it.

Unfortunately there will likely be unavoidable performance costs using it.
> I want someone to be able to define a class that has additional methods that can be called against the base object.

(3) Use a visitor pattern.
JLBorges wrote:
Use a visitor pattern.

That is not something I have ever heard of. Is this a good explanation of the concept?
https://stackoverflow.com/questions/10116057/visitor-pattern-explanation

There's a lot I don't know, and I won't say otherwise.
What I'm using this for: packets come in, and I want to have people write handlers that can optionally handle the packet, with their own predefined methods.

It looks like a visitor pattern is a better choice here. In Java, this is a lot easier and I can simply reuse use the existing object, with <? extends Base> generics. It actually is an "inheritance" issue -- the Base class has a lot of useful methods that should be callable from the instance of the subclass.

I'll have some real code and post it on github at some point so you can see what i came up with. Thanks for the pointers everyone (no pun intended)!
What I'd do is have an array of pairs, where the first member of the pair is a BPF filter and the second one is a function.
1
2
3
4
5
6
for (auto &h : handlers){
    if (!apply_bpf(h.first, packet))
        continue;
    h.second(packet);
    break;
}


https://en.wikipedia.org/wiki/Berkeley_Packet_Filter
The library, still in it's infancy, is here: https://github.com/rkuris/n2klib

I really wish the callback function end-users have to write already gets the correct subclass instead of having to call a constructor, but it's still not terrible.
Topic archived. No new replies allowed.