-
-
Notifications
You must be signed in to change notification settings - Fork 56
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
Use std::shared_ptr instead of raw pointer. #991
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #991 +/- ##
==========================================
+ Coverage 38.90% 38.92% +0.02%
==========================================
Files 58 58
Lines 4000 3990 -10
Branches 2199 2201 +2
==========================================
- Hits 1556 1553 -3
+ Misses 1101 1090 -11
- Partials 1343 1347 +4
☔ View full report in Codecov by Sentry. |
a85bfdf
to
ba825d4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using raw pointer is problematic as nothing prevent a user to create
a "consumer" on a temporary object, delete the object and use the consumer.Using a shared_ptr ensure us to the object is keep alive while a consumer
is using it.
This is not 100% true. An std::shared_ptr
can be created from a regular pointer using a custom deallocator. Thus changing the API only makes the requirements about the object lifespan more explicit but doesn't actually bring any guarantees with it. What it does is the programmer is relieved from explicit management of object lifetimes, however at the expense of some risk of memory leaks (since, unlike garbage collected languages, C++-with-exclusive-usage-of-std::shared_ptrs doesn't automatically take care of reference cycles).
IMHO, the current justification for this relatively big change is not compelling enough.
kiwix::Library lib; | ||
std::shared_ptr<kiwix::Library> lib; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could have avoided a lot of trivial changes below as follows:
std::shared_ptr<kiwix::Library> libShPtr;
// the purpose of this data member is to avoid changes in old code during an API change
kiwix::Library& lib;
LibraryTest()
: libShPtr(kiwix::Library::create())
, lib(*libShPtr)
{}
include/html_dumper.h
Outdated
@@ -33,7 +33,7 @@ namespace kiwix | |||
class HTMLDumper : public LibraryDumper | |||
{ | |||
public: | |||
HTMLDumper(const Library* library, const NameMapper* NameMapper); | |||
HTMLDumper(ConstLibraryPtr library, std::shared_ptr<const NameMapper> NameMapper); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, HTMLDumper
and OPDSDumper
could be replaced with free functions - they were implemented as classes for reasons unrelated to the goals and principles of object oriented design. Now, having them as classes enables creating the dumper at one place and using it later but do we really need that functionality? Isn't it better to require that they are used as if they were free functions (i.e use and discard immediately after construction while ensuring that the Library
and NameMapper
objects stay unchanged during that time)?
Indeed. But moving the constructor private and allowing the creation of the library only through a shared_ptr limits a lot this possibility. It is still possible to create a shared_ptr with a custom deallocator from
To have a reference cycles, we need to have object stored in the library which have reference to the library itself. I don't think it is the case and we don't allow user to put custom object in the library. |
That's correct and I never implied that it wasn't. My point was that |
All those dumper were not used by any of our other projects. They are only used internally, either by `Library::writeToFile` or the server.
0042cd5
to
8906af5
Compare
New version of the PR. Mostly the same strategy, but as agreed in #988 (comment):
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second commit (the one titled "Use std::shared_ptr instead of raw pointer.") is too big. I would have made that change in several smaller steps.
include/search_renderer.h
Outdated
std::string getHtml(); | ||
std::string getHtml(const NameMapper* mapper, const Library* library); | ||
|
||
/** | ||
/** | ||
* Generate the xml page with the resutls of the search. | ||
* | ||
* @param mapper The `NameMapper` to use to do the rendering. | ||
* @param library The `Library` to use to look up book details for search results. | ||
May be nullptr. In this case, bookName is not set in the rendered string. | ||
* @return The xml string | ||
*/ | ||
std::string getXml(); | ||
std::string getXml(const NameMapper* mapper, const Library* library); | ||
|
||
protected: // function | ||
std::string renderTemplate(const std::string& tmpl_str, const NameMapper* mapper, const Library *library); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to pass the name mapper and library objects by reference rather than by pointer thus meaning two things:
- both are mandatory (a pointer can be NULL)
- the called function doesn't store the address of those objects
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed const NameMapper*
to const NameMapper&
.
But as library can be nullptr, I have keep a raw pointer.
…ence. We want to be sure that `Library` actually exists when we modify it. While it is not a silver bullet (user can still create a shared_ptr on a raw pointer), making the `Manager` keep `shared_ptr` on the library help us a lot here.
Same as `Manager`, we want to be sure that `Library` actually exists when we use it.
8906af5
to
b13d3f8
Compare
Done. The commit is split in several ones. One noticeable change is that now |
@veloman-yunkan any feedback? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with a minor comment (mainly intended to serve as a proof that I looked at the changes) -
the description of the commit titled "Make the Server keep a shared_ptr instead of a raw Library pointer." was obviously copy-pasted but not fully updated.
We want to be sure that `Library` actually exists when we use it. While it is not a silver bullet (user can still create a shared_ptr on a raw pointer), making the `Server` keep `shared_ptr` on the library help us a lot here.
…nter. Same as for `Library`, we want to be sure that the `NameMapper` actually exists when the server is using it.
The why of this mutex is in `Library` is a bit complex. It has been introduced in c2927ce when there was only `Library` and no `std::unique_ptr<Impl>`. As introducing the mutex imply implementing the move constructor, we have split all data in `LibraryBase` (and keep a default move constructor here) and add the mutex in `Library` (and implement a simple move constructor). Later, in 090c2fd, we have move the `LibraryBase` to `Library::Impl` (which should have been `Library::Data`). So at the end, `Library::Impl` is never moved. We can move the `mutex` in it and still simply implement move constructor for `Library`.
As we now always use Library through a shared_ptr, we can make `Library` not copiable and not movable. So we can move back the data in `Library`, we don't care about move constructor.
As we enforce the use of Library through a shared_ptr, let's simplify user life (and code) with new "type".
By moving the nameMapper/library arguments in `getHtml`/`getXml` we avoid any potential "use after free" of name mapper or library as they are not stored.
b13d3f8
to
9166b67
Compare
Commit message rewritten. Time to merge ! |
Using raw pointer is problematic as nothing prevent a user to create
a "consumer" on a temporary object, delete the object and use the consumer.
Using a shared_ptr ensure us to the object is keep alive while a consumer
is using it.
But it prevent us to create plain instance on the stack.
Fix #988