Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
Use malloc instead of new to allocate in external C API function GetCurrentModDir #9
This fixes a legitimate bug in op2ext's external API. Perhaps we should role it into Outpost 2 before the next release of the game?
Also, not sure if we want to use static_cast there or c style cast. I think it is okay to call the static_cast here since the cast doesn't affect what is passed outside of the function and across DLL boundaries.
DanRStevens left a comment •
Change looks good.
You're probably right about rolling this out quickly. Without this fix, we could get module crashes when modules try to clean up memory.
Edit: I think you're right about using
I was thinking about this, and there may be another problem. The op2ext module is compiled separately from whatever client code that uses the returned string. As such we've crossed a module boundary between the point of
The proper way to do this, is likely to provide a Free method, which the calling code could pass the string to after it is done with it. The above is the reason why most libraries with dynamically allocated resources also provide a corresponding
This is also why a lot a APIs allow you to pass in a buffer to write into, which greatly simplifies memory management. However, then the called code is unable to resize the buffer, and so if the string is too big, it has to fail and somehow signal the error.
op2ext's public API is a fair mess overall considering how small it is.
When we went with GetGameDir_s, we used the buffer + size concept. It might be nice to stick with that here for consistency, but that would require deprecating the current function.
Maybe creating a free function would keep from deprecating, which would be nice.
Dan, do you have a preference? I'm not knowledgeable enough to care either way as long as it works.
Will probably abandon this branch and create a new branch when we chose a way forward.
Memory allocations concerns are always a pain for API design with C/C++.
Two major approaches:
For Windows, paths have a soft upper bound of
My default preference is for the first method, though I have a strong bias for efficiency which is not particularly relevant here. What's annoying about the first is there is typically an artificially imposed upper bound on resource sizes, used to keep code simple.
Here, I'm tempted to use the Windows
I suppose there is another alternative, which is sometimes used. Have the buffer owned and managed by the library, but allow reading to it by returning a (possibly read-only) pointer into the buffer. The calling code is free to read/use/copy the contents of the buffer. It doesn't not own it though, and can not free it. The buffer may have it's contents cleared/rewritten/invalidated by future calls to library methods. This is mostly appropriate when calls to library methods occur in some set pattern where the library can be sure it has an opportunity to clean up it's own internal buffer at some point and there is no need to have multiple copies.
For instance, each call to
One downside is the limit of a single buffer can cause re-entrancy issues in some cases. This is an issue if the same library method is called from two different routines, where one is nested inside the other. The inner method can invalidate the buffer used by the outer method. This can result in hard to understand bugs. The problem is made worse in a modular system where the inner method is dynamically determined, and may not be aware of the implementation of the outer method.
In the case of the OP2 module system, I'm not aware of any case where one module may depend on another module. They are generally independent of each other, with the only dependencies being between the modules of the game itself.
I'm kind of thinking this last option might actually be the most convenient.
Okay, well I guess we could mirror the GetGameDir_s call and create a GetCurrentModDir_s function. Below is the GetGameDir_s syntax. This would allow calling larger than max_path although a user could start with max_path for the buffer size.
We could then mark the old function as deprecated.
// Retrieves the current absolute directory of the Outpost 2 executable with a trailing slash.
This was referenced
Dec 21, 2018
Hmm, you know, if the function is going to continue to exist in deprecated form, I think I might still recommend merging this. It's broken either way, though it's probably less broken with this merge. At the very least, the discussion as to why it's broken can become a permanent part of the project's history.
I didn't voice this earlier, but my concern is if previous modules have employed the function knowing that it uses new instead of malloc, will we be causing harm in those DLL's memory management by switching it?
If someone wants to investigate, console modules that are probably worth reviewing would be cm1, cm2, and the tech tree mod that I forget the name of.
If you think it is still the right choice, then let's merge in.
Restoring branch until we make a choice.
It's hard to say how it would affect them without looking at both the source, and what the compiler did with it.
Though if they ignored the problem, and just leaked memory, then it really doesn't matter how we allocate it. That might very well be the case.
I'm uncertain where to find the source for those projects.
Arklon might have the source to cm1 and cm2. sirbomber would have the source for the other one. Would be nice to have both in the repository.
I could load the modules and set a breakpoint in the function GetCurrentModDir within op2ext. Then by loading Outpost 2 and using the mod for a bit, it should tell us if they are loading using the function or not. I haven't tried this before, but if the breakpoint triggers, then VS may let me see the assembly associated with the call across the module sort of like what I provided when we were troubleshooting.
Below is the complete list of mods I know about. It would be really nice if someone could take the time to test and document them all. Some sort of a la carte menu for people to download and mod the game with. Unfortunately if I recall correctly, many are broken.
After thinking about this some more, I think we should just leave the deprecated function alone. If someone properly cleaned up the new call, I don't want to break their work. If a new module is compiled, it needs to use the new function. Testing all these modules seems to be a lot of work for a deprecated function.
Which they can't!
Though, it may have the appearance of working, and possibly do the right thing by accident.
Ehh, that would be a major pain to track down source and test things. Particularly since I haven't heard from some of those people in years. Last I spoke to Eddy, he'd lost access to the Renegades source. And the method, with or without the patch, is broken anyway.
As much as I don't like to just leave things, I think you might be right. This probably isn't worth the effort to pursue further. Let's just close this.