-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error in (non-virtual) C++ multiple inheritance example (?) #39
Comments
My fault :-) I am, once again, astonished by some of the bugs I made back when I wrote the guide... You are absolutely right and there's really no good excuse for the code above :-) I suspect I meant to make a slightly more advanced example of single inheritance, not an example of multiple inheritance, so that I think the chapter should be renamed to "Multiple Times Single Inheritance" (or something like that) because I'm pretty sure that's what I tried to do and Doing C++ multiple inheritance examples in LLVM is likely extremely tedious and error-prone, so I don't think I'd like to take that task on my shoulders. |
Nah, just checked the guide. It very clearly differentiates between "multiple inheritance" and "virtual inheritance": I simply blew it once again ;-) I'll see if I can fix it pretty soon. Thanks a lot for the bug report! |
Uh, I'm looking at it right now. I must admit that my C++ is very weak nowadays (I code mostly in PHP, Python, and C# these days)... I don't even recall which variant of My immediate reaction to this is that the non-virtual variant of multiple inheritance should be removed. Personally, I see the handling of non-virtual overloading of data members as both archaic and arcane: Not something that's really used a lot anymore. I think it is outside the scope of this document as it requires a lot of bookkeeping when generating LLVM IR. What do you guys say? Are any of you willing to make the chapter work or should we simply delete it? |
I suggest merging the virtual and multiple inheritance chapters and discuss the various ways you can do it there. Also discuss the difference between the regular and virtual inheritance. I'd also add what clang is currently doing: %class.Derived = type { %class.BaseA, %class.BaseB, i32 }
%class.BaseA = type { i32 }
%class.BaseB = type { %class.BaseA, i32 } |
Agree. Doesn't seem so useful. But I think it might be good to discuss it and mention the caveats of doing this. |
👍 on archaic and arcane. That is: diamond shaped non-virtual inheritance (as in streams IO) may be well defined, but has long ago ceased to be an uncontroversial unique selling point of C++. |
A: fwiw - there is no precedence, the C++ compiler simply throws an error unless you specify which
clang++ 10:
|
In think the interface chapter could be elaborated a bit on. The gotchas with multiple (possibly) overlapping base classes are probably in getting the vtable layout correct and making sure that narrowing casts as well as dynamic downcasts use the right offsets. (To be honest, I always get lost in the weeds in this part of compiler design.) Here's a (not so) small example of multiple inheritance of abstract base classes (ie. Java #include <iostream>
struct Sender {
virtual ~Sender() = default;
virtual int send() = 0;
};
struct Receiver {
virtual ~Receiver() = default;
virtual void receive(int) = 0;
};
struct Filter : public Sender, public Receiver {
virtual ~Filter() = default;
virtual bool is_empty() const = 0;
};
inline bool is_odd(int value)
{
return (value % 2) != 0;
}
struct OddFilter : public Filter {
OddFilter() : empty_(true) {}
virtual ~OddFilter() = default;
bool is_empty() const override {
return empty_;
}
int send() override {
// assert(!empty_);
int result = data_;
empty_ = true;
return result;
}
void receive(int value) override {
// assert(empty)
if(is_odd(value))
{
data_ = value;
empty_ = false;
}
}
private:
bool empty_;
int data_;
};
int main()
{
Filter* f = new OddFilter();
Sender* s = f; // narrowing dynamic cast Filter* to Sender*
Receiver* r = f; // narrowing dynamic cast Filter* to Receiver*
for(int i = 0; i < 10; ++i)
{
// assert(f.is_empty());
r->receive(i);
if(!f->is_empty())
{
std::cout << "Received odd: " << s->send() << std::endl;
}
}
delete f;
} I admit that it's a contrived example, but maybe it can help as a basis? I like that the book so far uses very short to the point examples so please feel free to alter/skin/ignore this darling 😳 |
Hi Michael,
Thanks for the nice book, I enjoyed reading!
I think the
%Derived = type { ...}
example in multiple interitance misrepresents the C++ example it tries to model.The C++ code:
essentially creates two instances of field
_a
. This quirky multiple inheritance interpretation in C++ renders_a
inDerived
ambiguous, and requires disambiguation likeBaseA::_a
orBaseB::_a
inDerived
. The example llvm code, however,merges both instances of_a
:While this might be what is generally desired, it does not reflect the example C++ semantics. I think it should look something like:
(and this fix would obviously breaks a bunch of the setters that should be adjusted accordingly)
Btw. another possible improvement would be to use different types for
_a
,_b
and_c
in the example (u32
,float
,i32
) as a way to emphasize implicitly which field we're setting.Hope this helps, t
The text was updated successfully, but these errors were encountered: