When your question is answered use !solved to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
160 messages · Page 1 of 1 (latest)
When your question is answered use !solved to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
i'm not sure i understand the problem
just implement the method in the base class to throw whatever you want it to throw?
personally I don't understand the desired feature description
wanting to error if "the base version is used, and detecting that at compile time", is essentially a non-sense when it comes to virtual functions
either the method exists, or it doesn't
you can't have it sorta not exist but it's fine if you use it anyways, except it's not fine, so it'll throw. like wat?
one of the key feature of virtual function is that you do not know the concrete type you're manipulating, just that it derives from the base and the base has some virtual functions
in those usage contexts, you can not ever know if the base version will be called, or a different one
because you don't the most-derived type of instance that you'll manipulate
in order to enable that kind of compile-time diagnostic, you must know the concrete, most-derived type
if knowing that concrete, most-derived type, is an acceptable constraint/context, you can always opt into using a bunch of templates
at which point you will only trigger errors if you try to instantiate a template that tries to use a member function that doesn't exist, and you're free to provide/not provide that member function so long as the bits that need the member function to exist, do not end up being called
that last sentence sounds weird but I understand myself, which is something
i understand it. but i'm not sure it'll be very understandable 😛
guess it's example time then...
I'm in a legacy code base where virtual functions probably shouldnt have been used at all because everything is known at compile time
but im kind of stuck with the pattern
;compile
#include <iostream>
template <typename T>
void use_foo(T&& t) { t.foo(); }
template <typename T>
void use_bar(T&& t) { t.bar(); }
struct Fooable
{
void foo() { std::cout << "only foo!\n"; }
};
struct FooBarable
{
void foo() { std::cout << "FooBar foo!\n"; }
void bar() { std::cout << "bar!\n"; }
};
int main()
{
Fooable f;
FooBarable fb;
use_foo(f);
use_foo(fb);
use_bar(fb);
// use_bar(f); // <-- problem
}
only foo!
FooBar foo!
bar!
depending what you can/cannot do, you may or may not be stuck
to what extent do you have control over all the derived classes, and what's the current existing thing/interface
I'm able to make new ones but cant really change existing ones
if you don't ever actually need/use the polymorphism, you can just work as if you didn't have virtual functions
up to having to deal with already existing pure virtual functions that force you to implement them despite them not doing anything
I don't want to turn the base class into an abstract class
Why do you not want it to be an abstract class?
The actual problem I'm solving is that I've made some new derived types with some static constexpr metadata on them, and I'm stuck with the getters for that new static metadata being virtual function calls by pointers typed to the base class.
But why does the base class need to be concrete?
I don't see how that's a problem
because I cannot alter existing derived classes to implement the getter for this new metadata and making the base function pure virtual makes them stop working
if you know the concrete type then you don't actually end up having to virtual dispatch the thing
Do you want to have instances of the base class? Then you could go with the exception throwing version and make sure derived classe overrides do not call into Base::Method.
what?
Then throw an exception in the base class function?
the new derived types have the static constexpr stuff, and to abide to the existing interface you have existing virtual getters
that you've implemented to return a ref/copy of the static constexpr stuff
so why do you need the getters to be pure virtual
if you added the getters, why are they virtual in the base
and not just non-virtual in the derived types
On the front end I have pointers with the type of the base class and I have to rely on the new functions being virtual so they can be called properly. I can't change this.
you said the types are known at compile-time
yeah i don't get that either. you either have that data on all objects of that kind, or you don't.
then you don't know the types at compile time
What I'm saying is that reasoning about the code I do, but the way its written doesnt assume that.
sorry if I'm being unclear
like, if I could rewrite more than I am able to I could just make this compile time
if you know the exact type, why do you not have that exact type?
why do you have a base type instead
thats a good question! Legacy code base issue
there's something that's fishy about this whole thing, the preferred way to use polymorphism is to ask the polymorphic type to do the thing, not grab information out of the polymorphic type to do different things
yeah
if the getter doesn't make sense for all derived types, then in what context do you actually need to get/grab the information
like, dunno, if you can change neither the interface nor the code that uses the interface then i have no idea what you're hoping to achieve. you're gonna have to change something in order to change something.
instead of having a virtual function that let the type do its thing, possibly using its internal data as it wishes to
I'm not gonna say that it never happens that you need to downcast or grab information that only makes sense in specific cases
but ideally you try to avoid that
then if you're stuck with that then you essentially just do it
pseudo code for the front end is
&base pointer = datastructure.getclass()
pointer.getmetadata()
because the front end interface I've got is that &base pointer, as far as I'm aware, I need to use a virtual function here?
why do you need to get the metadata out of the object in the first place?
and how much control do you have on getclass
and how specific is this snippet of code
if you're in a very specific derived class context, you can use covariant return types
like, whenever you're writing a method whose name starts with get or set, that is a moment to pause and think real hard because you're likely in the process of making a mistake
if you're getting or setting data on objects, that means your objects are relegated to being mere data containers. there's no encapsulation anymore. the actual logic of what to do with state is now in the code using the object as opposed to in the object.
this is some weird legacy stuff with passing data from a c++ backend to a c# front end with managed c++ so like, the backend c++ stuff is basically just a data container yeah
if it's basically just a data container then why the polymorphism?
I can't change design decisions from 20 years ago here without rewriting everything
tl;dr
well, we're gonna have to understand what problem you're trying to solve
otherwise, there's nothing we can do to help you
Ok so this snippet
&base pointer = datastructure.getclass()
pointer.getmetadata()
.getclass is always going to return a pointer to a derived type that implements the new function
well yeah, then that function will have to be virtual 🤷♂️
which means all derived classes will have to implement it
ideally I'd be able to just get a pointer directly to the derived type here instead and skip virtuals all together
do you know for sure that this is always a specific type in this case?
ok but what is getclass/datastructure
it should always be from the subset of derived types that implement the function and if it doesnt that should throw an error
if datastructure for whatever reason could return a variety of derived type, you cannot just assume you'll always be in a context to be able to just call getmetadata
like, given that you're already royally screwed design-wide, you could just dynamic_cast
which ofc is utterly terrible
but here we are
a bunch of bullshit that brings data from the backend to the c++ / CLI middle layer
if it's completely not an option for you to change anything then dynamic_cast is the only thing you can do
yes I was considering a dynamic cast
subclass the original base class and let getclass return that subclass
ofc that only makes everything worse
but the alternative will be actually fixing this design
if you say you can't do that, well…
so theres the base class
theres a bunch of derived types
then theres this final layer of derivation where I take some of those derived types and give them some extra static metadata
all types with the extra metadata are derived from types that are identical except for lacking that metadata, and all those derived types are part of the existing inheritance tree
why does the metadata need to be attached by deriving from the types without metadata?
you could instead have, e.g., a wrapper template that just adds that metadata around a given type
and then that template could inherit from some base class interface that's shared by all wrapped types
and so instead of making the original hierarchy even worse than it already is, you've made a separate hierarchy for stuff with metadata
no idea if that would work in your case ofc
since i have no idea what your stuff is doing
If there's a better way to do it then I'm open to hearing it!
Currently the derived types have 1-3 kinds of valid metadata for them, and there are many many instances and in the current design the metadata is stored per instance. This has become a genuine performance problem from storing thousands of redundant strings.
I made a template that derives from the existing derived types and accepts the constexpr metadata strings as template parameters so I can move that metadata from being per isntance to being static.
I did this because the rest of the code base doesnt seem to notice that change and it quite happily works with the new derived types with static metadata as it did with the old system where its per instnace.
so you have tons of instances that all have the same metadata?
have you considered, e.g., instead of attaching the metadata to each instance, just having a dictonary of metadata keyed on the pointer to the object?
well what I'm currently doing instead is making a derived type per metadata (with the template) and defining the metadata as compile time static constant in those types.
but you said the original design already had metadata per object
so how was that metadata accessed?
the original design has metadata per instance and used the getters in the base class
well then continue doing that?
just implement the getters such that they return your string literal or smth instead of a per-class copy of a string 🤷♂️
so everything works with the templated solution, I'm just trying to remove the implementation of the getter from the base class so it stops being used and if someone does use it then the compiler complains at them if they did it in a static context and they get runtime errors if they try it otherwise.
i.e you shouldnt be trying to access the metadata unless you're working with a derived type that has metadata defined in it
i don't understand
the existing system has a getter for the metadata
and is using that
so just keep using that?
if the problem you're trying to solve is move from per-instance to per-class metadata, just change the implementation of the getter accordingly
and you're done
anything else will require exactly the kind of changes you insist you cannot make
so the existing system has a virtual getter on the base class, and I'm still using that. but I want to assert that when that virtual function is called that it resolves to a derived implementation of it, not the base one.
because the base one just returns a null pointer now
well then assert(false) in the base implementation 🤷♂️
but I want to assert that when that virtual function is called that it resolves to a derived implementation of it, not the base one.
When you call the virtual function it will already only call the derived implementation?
because for all I know someone could create an instance of the base class directly and then they cause a problem where the front end tries to access non existant metadata
Obviously assuming that you have overridden the virtual function in every derived class and that they do not delegate back to the base class version.
Then assert or throw exception.
why would someone create an instance of that base class?
Ok so that is the best way to go ahead in this instance?
Ok sorry, I think I got a bit stubborn on this because it seemed like there would be something more elegant to do than that.
this design is already utter crap. there's no elegance to be found there i'm afraid ^^
it gets used in some low level stuff I don't understand
but importantly isnt trying to access metadata in that context
and shouldnt be either
lol
void Base::Method()
{
throw std::exception{ "not implemented; Method can only be called on derived classes that override it."};
}
but I need it to throw an error if they try
yeah this is terrible. but there's pretty much nothing you can do without completely changing that design.
yeah, assert(false);
ok cool, if that is indeed the case I shall just assert then
if you only have a base type to work with, there's generally nothing you can do at compile time
as @warm wagon explained
your code is inherently built on resolving things at runtime
ideally I'd have made a metadata wrapper and put the existing types in that wrapper but I'd need to rewrite so much stuff to implement that that it wouldnt be worth it
Exception better if you are writing a library that is only shipped in release mode.
Otherwise assert is fine.
the templating solution I'm using is just for transparency with respect to the existing code base
since everything "looks" the same to the front end and I'm effectively just changing an implementation detail
Actually, I might be able to make a metadata wrapper class and then have the front end create pointers to that metadata wrapper instead 🤔
its only shipped in release mode so I'll go for exception then thanks
Well if the lib is only used by an app then no need because you would have made sure that the assertion never occurs in debug mode.
well
that's not exactly something you can rely on if you don't know the actual context/architecture
especially because they ship polymorphic types
Unless you have some dynamic scripting component, perhaps.
there's generally nothing that prevents a client that also codes to derive from your interfaces and inject their own derived types in your code
because that's one of the point of polymorphic types
well maybe not one of the "point" but one way they are often used
Conditional 'final' needed.
@last plover Has your question been resolved? If so, type !solved :)
!solved
Thank you and let us know if you have any more questions!
This thread is now set to auto-hide after an hour of inactivity
bruh...
std::raise(SIGSEGV);
assert doesn't exist in release builds
lol