-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
ofVideoPlayer using fs::path #8138
Conversation
For the parts where you just add override it is ok. but where you change from string to fs::path I would rather deprecate the function with string and make a new one with fs::path so this does not break other classes that might inherit from any of these. |
@dimitre looks good to me! |
I insist that this should not be done this way. and this merge needs to be reverted. Saying that you can offer updating the addon is not the correct solution, since that is one of the many derived classes where it could happen. OF has a lot of important issues that need to be fixed right now, and introducing this big source of problems with literally zero benefits is really unnecessary. I say zero benefits because no where in the updated code is making use of the useful functionalities that of::filesystem::path has. I am not sure if it is possible to have two functions named the same, like @dimitre @ofTheo I have to insist, we need to bring this back. |
This reverts commit 2bd8b45.
Guys, @dimitre @ofTheo @ofZach @bakercp @artificiel @2bbb @danoli3 BTW, I do have the privileges needed to just go and merge my PR, but I'd rather be respectful and find out your point of view before doing such |
@roymacdonald I think it would be helpful to describe what about this PR is a breaking change? A list of issues or regressions caused by this PR would be really helpful. ps: One of the reasons we're trying to move towards fs::path is so that OF can better support a wider range of languages / locales. If you have suggestions to making the switch in a cleaner way would be great to hear too! Thanks so much! |
Yes this is a breaking change. |
I'm good if it is decided differently but I think it is important because all changes to make it work internationally are already ok
|
Thanks @dimitre !
Thanks! |
not breaking in the sense of returning paths, it will work the same as before. only to derived classes like ofxHapPlayer. |
I am aware of how helpful it can be to have better locale support, but if that breaks previous code is not good. So, in this case, any class derived from ofVideoPlayer will break. Let alone fixing ofxHapPlayer, it is not about the specifics but rather the design choices. If you do it for this then you will do for anything else. So, in the case of just throwing in fs::path as a direct replacement for std::string as the argument type in any virtual function is a bad idea, since the virtual function is meant to be overriden, thus it will break any class that does so. Then, also changing the return types of functions from std::string to fs::path will break stuff. The same happens for public and protected class members. Being able to run some old code without doing any change. in your current OF setup is really nice. But now , one of the really interesting features of OF is the amount of addons available. A lot have not been updated for quite a while and still work fine, but because of these changes these will not. Also, a lot of the issues that I've been fixing in PG (which is quite broken ATM) come from just throwing in std::filesystem::path as a direct replacement for std::string, which is not. I see how useful it is to have std::filesystem::path but because of its introduction everything breaks is a bad idea. So, I suggest that we revert all the changes that introduced std::filesystem::path in the master branch, and we keep those in a development branch. Then, once we are happy with all these we can merge, but we do it all at once and not as random commits that are all over the place. |
Thanks Roy - I think I wasn't aware of all the issues with inherited classes ( especially when we are making changes in classes that are designed to be inherited ). For the ofVideoPlayer use case ( as an example ) is there a way to keep a legacy virtual function that can still allow un-updated addons to work with a deprecated warning? ie:
Or will these be too similar and result in errors due to ambiguity? |
Diving more into this and with some help from chatGPT - it looks like we could use a proxy class to essentially support both string returns and of::filesystem::path returns across all our classes. It's definitely a bit more complicated but it could in theory support people doing:
even maybe:
Curious what you think @dimitre @roymacdonald @artificiel ? Ideally we could modernize while maintaining backwards compatibility. |
std::string myPath = ofToDataPath("/some/path/file.ext"); works, as it is equivalent to: std::string myPath = ofToDataPath("/some/path/file.ext").string(); changing the signature of methods is still a valid problem, as pointed out, specifically for virtual ones. perhaps there is a transitory approach. @roymacdonald near the top in this issue suggested not to change the signatures but to deprecate the movie.load("my.mov"); // will generate a deprecation warning as so maybe adopting a 3 signatures pattern and implement things like: [[deprecated("favour of::filesystem::path for path representation")]]
bool loadMovie(std::string filename) {
return loadMovie(of::filesystem::path(filename));
}
bool loadMovie(const char * filename) {
// bypasses char->string conversion for end-user convenience in C++ source text
return loadMovie(of::filesystem::path(filename));
}
bool loadMovie(of::filesystem::path() & path) {
// actual implementation;
}
someone writing std::string mov {"my.mov"};
movie.load(mov); // will show the deprecation, which will make sense as the type is explicitly stated
//
movie.load("my.mov"); // no deprecation and the char * is converted to fs::path so ofxHapVideoPlayer works, nothing has to change in what is already implemented out there, and |
I'd also like to take the occasion to point out that a larger-scale issue is raised here by @roymacdonald relating to management of the master branch (how to organize intent, momentum, testing, information dissemination and the high-level decision process of balancing moving ahead and keeping things the same). it needs another context for discussion, and it is not clear that the GitHub Issues (or Discussions) is the ideal one as there are many considerations that are easy to have out of focus, then at some point brought back, leading to dead-ends as unravelling the "significant meat" of prior exchanges becomes more complicated than taking the actual decisions. I'm not suggesting anything just agreeing that the management of /master is not ideal and needs a refresh. I'm also of the opinion that things must evolve as the C++ core is more and more smelling of an old timer's dorm, which is not conducive to rally new users. |
@artificiel thanks for this input! That's super helpful.
@roymacdonald would something like the above address the inherited class concerns with the PR? |
Thanks @ofTheo and @artificiel for your replies. Although, this
does not. It ends in an infinite loop if I pass a cstring literal. I kinda like the idea of a proxy class. I will take a look at it. Ideally, having classes like ofVideoPlayer, where load function is virtual , add a deprecation warning if the string version of it is being used, but still alllow it to work. Probably using some sort of template if_enabled condition could help. I also found that following breaks now:
Which makes sense. |
As for @artificiel mentions about the use of branches, I agree, that goes elsewhere, nevertheless it needs to be addressed. |
@roymacdonald it should not require templates; is it possible that your test methods were not fully qualified? (the code above was written in-github-issue so untested etc and also I used the already-deprecated [[deprecated("instead of std::string favour of::filesystem::path for path representation")]]
bool ofVideoPlayer::load(std::string filename) {
return loadMovie(of::filesystem::path(filename));
}
bool ofVideoPlayer::load(const char * filename) {
// bypasses char->string conversion for end-user convenience in C++ source text
return loadMovie(of::filesystem::path(filename));
}
bool ofVideoPlayer::load(of::filesystem::path() & path) {
// actual implementation;
} works here. with some logging it shows:
|
I tested a general case function, having the same name. But different arguments, since we might have to apply the same to so much more classes. It ends in an infinite loop. My test was vague but regardless of it it failed and that proves that it needs to be handled carefully. |
I'm sorry if I'm not understanding what's being proven here; can you share your test code? |
also @dimitre: could you perform a quick audit of the virtual functions in OF core that are affected by the change from |
@artificiel not any other virtual function I remember. |
a bit of grep action revealed 3 files with methods with such virtual interfaces: |
After thinking about it a bit I think the video player should take both string and filesystem args without deprecating one over the other. A lot of users will be doing But having filesystem::path for say dragging a file into a window is great - because there will be less issues with international filesystems. So maybe we just need the two functions without the deprecation? |
I'm 50/50, as there are other advantages to be aware of to be sure though, adding and then should these overridden methods be re-virtualized at the ofVideoPlayer level? (that's what ofxHapPlayer does). (also note that HPVPlayer does not bother inheriting ofBaseVideoPlayer; it almost conforms but changed some return types). BUT it raises another point: if the new |
currently these are the methods to be dealt with:
|
I now can see the ambiguities mentioned by @roymacdonald |
great @artificiel. I think at least the functions in ofURLFileLoader.h doesn't need to be virtual. |
and also perhaps just for the sake of the deprecation-or-not discussion: while paths are easily represented by a string (and yes "it currently works"), a filesystem path is not just a string — it is an object with a specific intent. this topic is important to be discussed within the learning process (all examples should be upgraded to path). it's not just about path vs string, but reasoning about objects and how the type of an object informs about it's function. also it's one thing to type a bunch of std::strings in source to load static assets, but any code relying on "getting a path" should be returning a ::path (including ofToDataPath). if std::string is propped around as an OK equivalent container for paths, risks are high that they will get converted implicitly and users will be making path->string->path travels without noticing. |
the inheritance of ofVideoPlayer can be greatly simplified. I studied this classes in the past. |
Yes definitely! That was my thinking too @artificiel |
@dimitre hm that's strange too: |
@artificiel thanks for digging into this. It is really interesting that ofVideoPlayer does not inherit from the ofBaseVideoPlayer. |
@roymacdonald yes there is a design question also maybe in the naming -- "base" suggests functionality-to-be-expanded while "abstract" suggests interface enforcement, with required methods marked pure. Seems these "base"-named classes are more of an abstract nature (that's obviously not black vs white but in the spirit of self-documenting design, it helps to have clear names). And it seems in the case of ofVideoPlayer that the interface was respected, but without inheriting anything. Beyond not making things explicit with override, it also breaks polymorphism (if that is to be expected between of(Base)VideoPlayer subclasses, maybe not... again it's more of a decision than requirement). |
@artificiel Sure. It is just strange. Although I dont clearly see why having ofVideoPLayer to subclass ofBaseVideoPlayer will break polymorphism. |
I too agree ofVideoPlayer should inherit less classes. maybe ofBaseDraws and not much more. |
yes ofBaseVideoPlayer has no implementation whatsoever — but inheriting an abstract class is not about bundling functionality but: (a) signifying the adhesion to an interface, and fulfill some requirements (pure virtual) (b) allowing the dynamic typing of overridden methods. So for instance (pseudo-code) std::vector<std::shared_ptr<ofVideoBasePlayer>> players;
players.push_back(make_shared<ofVideoPlayer>());
players.push_back(make_shared<ofHapPlayer>());
for (const auto & p: players) p->draw(); // the overloaded draw will be called, even if p is of `ofVideoBasePlayer` class in this case (a) is more important. that being said, maybe it's not meant to be inherited but it's really a mystery as to why the class is obviously written as compliant, but not inherited. |
I think that it works fine. It´s been like this for quite a while and doing any changes might lead to trouble. I think that we should leave it as is and focus on other issues that are way much more important. :) |
this PR intends to use fs::path internally for video player functions
(load, loadAsync, loadMovie [[deprecated]] )