How do you use a file to initialize a global const object that is too large to be created in the stack? This is my attempt so far:
// test.h
#pragma once
#include <boost/array.hpp>
typedef boost::array<int,100000> bigLut_t;
extern const bigLut_t constLut;
// test.cpp
#include <fstream>
#include <boost/filesystem.hpp>
#include "test.h"
bigLut_t& initializeConstLut()
{
if( boost::filesystem::exists("my_binary_file") == false ) {
std::ofstream outStream( "my_binary_file", ios::out | ios::binary );
bigLut_t* tempLut = new bigLut_t;
for(int i = 0; i < 100000; ++i) {
// Imagine this taking a long time,
// which is why we're using a file in the first place
tempLut->at(i) = i;
}
outStream.write( reinterpret_cast<char*>(tempLut), sizeof(bigLut_t) );
outStream.close();
delete tempLut;
}
// We can't write "bigLut_t lut;" because that would cause a stack overflow
bigLut_t* lut = new bigLut_t; // lut gets never deallocated
std::ifstream inStream( "my_binary_file", ios::in | ios::binary );
inStream.read( reinterpret_cast<char*>(lut), sizeof(bigLut_t) );
inStream.close();
return *lut;
}
const bigLut_t constLut = initializeConstLut();
AFAIK this works in a sense that constLut gets corretly initialized, but there's a memory leak since bigLut_t* lut gets never deallocated. I tried using a smart pointer for it, but that resulted in the values in constLut being quite random. I'm baffled by the lack of information I found by trying to google the solution.
How did you use shared_ptr ? Try the following :
// test.h
#pragma once
#include <boost/array.hpp>
typedef boost::array<int,100000> bigLut_t;
extern const bigLut_t constLut;
// test.cpp
#include <fstream>
#include <boost/filesystem.hpp>
#include "test.h"
boost::shared_ptr<bigLut_t> initializeConstLut()
{
if( boost::filesystem::exists("my_binary_file") == false ) {
std::ofstream outStream( "my_binary_file", ios::out | ios::binary );
bigLut_t* tempLut = new bigLut_t;
for(int i = 0; i < 100000; ++i) {
// Imagine this taking a long time,
// which is why we're using a file in the first place
tempLut->at(i) = i;
}
outStream.write( reinterpret_cast<char*>(tempLut), sizeof(bigLut_t) );
outStream.close();
delete tempLut;
}
// We can't write "bigLut_t lut;" because that would cause a stack overflow
boost::shared_ptr<bigLut_t> lut(new bigLut_t); // lut gets never deallocated
std::ifstream inStream( "my_binary_file", ios::in | ios::binary );
inStream.read( reinterpret_cast<char*>(lut), sizeof(bigLut_t) );
inStream.close();
return lut;
}
const bigLut_t constLut = *(initializeConstLut().get());
There are several good solutions to your problem. The solutions offered as answers so far are not among the good solutions (in particular, dynamic allocation and copying and relying on lifetime of temporary objects is just Very Bad™). I'll just give you one common solution.
A simple way to provide a huge constant is to use a Meyers' singleton, which means defining the constant as a static local variable in a function that returns a reference to it:
inline BigThing const& theBigThing()
{
static BigThing const theInstance; // Default constructor does the init job.
return theInstance;
}
This is not yet a complete solution, but let's first see how you can get rid of having to invoke a function, and instead dealing directly with what-looks-like-a-constant:
namespace detail {
inline BigThing const& theBigThing()
{
static BigThing const theInstance; // Default constructor does the init job.
return theInstance;
}
}
BigThing const& theBigThing = detail::theBigThing(); // No copying, just ref.
In your case where BigThing is an array that should be initialized from data in a file, you can't directly rely on the default constructor. You could if you defined a wrapper class, and that's one way. OK, let's do that (it's what I'd choose, I think):
namespace detail {
struct BigThingWrapper
{
BigThing thingy_;
BigThingWrapper()
{
// Initialize the thingy_ member here.
}
};
inline BigThing const& theBigThing()
{
static BigThingWrapper const theInstance;
return theInstance.thingy_;
}
}
BigThing const& theBigThing = detail::theBigThing(); // No copying, just ref.
Note 1: I've used inline so that the code conceivably could be placed in a header file. Just remove for implementation placed in an implementation file.
Note 2: This code is untouched by compiler's hands and so may contain ERORS, TYPPOS and whatnot. :-) But it's the idea that is your answer.
Note 3: There are, as mentioned, also other good ways to do this, so this is not "the" answer, and there is no "the" answer, but it's "an" answer.
Cheers & hth.,
Just doing it the old fashioned way is fine - create an auto pointer whose lifecycle is the whole application life-cycle, and an extern reference to the pointer:
// test.h
#pragma once
#include <boost/array.hpp>
typedef boost::array<int,100000> bigLut_t;
extern const bigLut_t& constLut; // make it a reference
// test.cpp
#include <fstream>
#include <boost/filesystem.hpp>
#include "test.h"
namespace {
std::auto_ptr<bigLut_t> initializeConstLut()
{
std::auto_ptr<bigLut_t> lut(new bigLut_t);
if( boost::filesystem::exists("my_binary_file") == false ) {
std::ofstream outStream( "my_binary_file", ios::out | ios::binary );
for(int i = 0; i < 100000; ++i) {
// Imagine this taking a long time,
// which is why we're using a file in the first place
lut->at(i) = i;
}
outStream.write( reinterpret_cast<char*>(lut), sizeof(bigLut_t) );
outStream.close();
// no point writing then reading the same data
} else {
std::ifstream inStream( "my_binary_file", ios::in | ios::binary );
inStream.read( reinterpret_cast<char*>(lut.get()), sizeof(bigLut_t) );
inStream.close();
}
return lut;
}
// local to this compilation unit, deletes object on exit
std::auto_ptr<bigLut_t> constLutPtr ( initializeConstLut() );
}
// the extern reference refers to the object held by the auto_ptr
const bigLut_t& constLut ( *constLutPtr.get() );
No extra copying, client code sees the extern variable as before, though the linker may have to have an extra indirection rather than the extern varibale being in a fixed address (&constLutPtr is on the heap rather than in static data area).
If having a fixed address for constLut is important, go back to having an extern value rather than an extern reference, read the data in using a reinterpret cast and a const_cast of &constLutPtr. pass reinterpret_cast<char*>(const_cast<bigLut_t*>(&constLut)) to the stream read.
Wouldn't it be easier to just make your global object a pointer, new a temporary object from initializeConstLut and set the global pointer to that object before exiting the function ?
If the object is so big, you should avoid any solution which involves copying and/or assignment. So you're left with several choices :
Make the global object a pointer (free copy)
Make the global object non-const and read directly in it
I can't believe I'm saying this, but use a Singleton like pattern and wrap the object. This will allow you to isolate initialization and provide a public method for access which will return a const reference to the array.
I'm not adding much details here because it really depends on your requirements (I'm thinking thread-safety for instance).
There is a line of thought that says known objects do not leak, if the only leak is at the end of application life. If all you do with the object is remove it from memory (and do not release other resources, nor update some external stuff like DB's and files) then you can just leave it there. After all, by explicitly deleting them from memory you are just blocking the application from finishing for no real benefit: memory will be released one way or the other.
Add an explicit destruction of the lut. You are responsible for destroying anything you create. Best is to do it explicitly:
void destroyLut(bigLut_t& lut)
{
delete &lut;
}
Then simply call this function before exiting main. You should never rely on static initialization and destruction in C++. Better is to always do explicit initialization and destruction.
Related
Hopefully my title isn't too confusing. I'm trying to write a sound manager for my game using SFML. I'm trying to replace my new/delete with the "smart pointer" std::shared_ptr. This is what I have so far.
/* SoundManager.h */
#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H
#include <SFML/Audio.hpp>
#include <string>
#include <memory>
class SoundManager
{
public:
~SoundManager();
struct jteSound
{
sf::Sound snd;
sf::SoundBuffer sndBuffer;
std::string name;
};
//Load a new sound from path and store it in audio bank bnk.
//Banks are simply std::vectors of type jteSound.
void registerNewSound(std::vector<std::shared_ptr<jteSound>> &bnk, std::string path, std::string sndName);
//Footsteps bank
std::vector<std::shared_ptr<jteSound>> bnkFootsteps;
};
#endif // SOUNDMANAGER_H
/* SoundManager.cpp */
#include "SoundManager.h"
#include <stdlib.h>
SoundManager::~SoundManager()
{
/*
//Cleanup each sound bank that we use.
for (std::vector<jteSound*>::iterator it = bnkFootsteps.begin(); it != bnkFootsteps.end(); ++it) {
delete *it;
}
*/
}
void SoundManager::registerNewSound(std::vector<std::shared_ptr<jteSound>> &bnk, std::string path, std::string sndName)
{
static int counter = 0;
for (int i = counter; counter <i+1; counter++) {
bnk.push_back(jteSound);
bnk[i]->name = sndName;
bnk[i]->sndBuffer.loadFromFile(path);
bnk[i]->snd.setBuffer(bnk[i]->sndBuffer);
}
}
bnk.push_back(jteSound); gives me a compiler error. If I remove the line, the program compiles, but crashes. I have tried things like emplace_back() or jteSound* or new jteSound, but nothing works. I always get a lengthy compiler error or immediate runtime crash. When I use regular pointers and new/delete, see https://bpaste.net/show/fa684f2f2d5e and https://bpaste.net/show/c74ac701ce7a, the code works as expected. Any thoughts appreciated!
The type of the elements inside your std::vector is std::shared_ptr<jteSound> which means that std::vector::push_back will accept only instances of that type.
To make your code work you have two options. The first is using std::make_shared helper function as follows:
bnk.push_back(std::make_shared<jteSound>());
// the equivalent counterpart is:
bnk.push_back(std::shared_ptr<jteSound>(new jteSound));
The second is using std::vector::emplace as follows:
bnk.emplace(bnk.end(), new jteSound);
As the comments below warn, using the second option is risky because it can cause memory leak when the new jteSound succeeds but the std::vector::emplace has to reallocate memory and fails.
What are good practice options for passing around objects in a program, avoiding accessing non initialized member variables.
I wrote a small example which I think explains the problem very well.
#include <vector>
using namespace std;
class container{public:container(){}
vector<int> LongList;
bool otherInfo;
};
class Ship
{
public:Ship(){}
container* pContainer;
};
int main()
{
//Create contianer on ship1
Ship ship1;
ship1.pContainer = new container;
ship1.pContainer->LongList.push_back(33);
ship1.pContainer->otherInfo = true;
Ship ship2;
//Transfer container from ship1 onto ship2
ship2.pContainer = ship1.pContainer;
ship1.pContainer = 0;
//2000 lines of code further...
//embedded in 100 if statements....
bool info = ship1.pContainer->otherInfo;
//and the program crashes
return 0;
}
The compiler cannot determine if you are introducing undefined behavior like shown in your example. So there's no way to determine if the pointer variable was initialized or not, other than initializing it with a "special value".
What are good practice options for passing around objects in a program, avoiding accessing non initialized member variables.
The best practice is always to initialize the pointer, and check before dereferencing it:
class Ship {
public:
Ship() : pContainer(nullptr) {}
// ^^^^^^^^^^^^^^^^^^^^^
container* pContainer;
};
// ...
if(ship1.pContainer->LongList) {
ship1.pContainer->LongList.push_back(33);
}
As for your comment:
So there are no compiler flags that could warn me?
There are more simple and obvious cases, where the compiler may leave you with a warning:
int i;
std::cout << i << std::endl;
Spits out
main.cpp: In functin 'int main()':
main.cpp:5:18: warning: 'i' is used uninitialized in this function [-Wuninitialized]
std::cout << i << std::endl;
^
See Live Demo
One good practice to enforce the checks is to use std::optional or boost::optional.
class Ship
{
public:
Ship() : pContainer(nullptr) {}
std::optional<container*> Container()
{
if(!pContainer)
return {};
return pContainer;
}
private:
container* pContainer;
};
It will force you (or better: provide a firm reminder) to check the result of your getter:
std::optional<container*> container = ship1.Container();
container->otherInfo; // will not compile
if(container)
(*container)->otherInfo; // will compile
You would always need to check the result of operation if you use pointers. What I mean is that with optional the situation is more explicit and there's less probability that you as the programmer will forget to check the result.
It seems that you are looking for a way to make your code
bool info = ship1.pContainer->otherInfo;
work even though the pContainer may be null.
You can use a sentinel object, which holds some default data:
container default_container;
default_container.otherInfo = false; // or whatever the default is
Then use a pointer to the sentinel object instead of a null pointer:
//Transfer container from ship1 onto ship2
ship2.pContainer = ship1.pContainer;
ship1.pContainer = &default_container; // instead of 0
//2000 lines of code further...
//embedded in 100 if statements....
bool info = ship1.pContainer->otherInfo;
If you use this, you should make sure the sentinel object cannot be destroyed (e.g. make it a static member, or a singleton).
Also, in the constructor, initialize your pointers so they point to the sentinel object:
class Ship
{
public: Ship(): pContainer(&default_container) {}
...
};
I found an additional solution. It is admittedly not preventing the access of uninitialized objects, but at least the program crashes AND returns an error message, that enables us to correct our mistake. (This solution is particularly for the g++ compiler.)
First of all set the compiler flag _GLIBCXX_DEBUG. Then instead of naked pointer use unique_ptr.
#include <vector>
#include <iostream>
#include <memory>
using namespace std;
class container{
public:container(){}
int otherInfo = 33;
};
class Ship
{
public:Ship(){}
std::unique_ptr<container> upContainer;
};
int main()
{
Ship ship1;
cout<<ship1.upContainer->otherInfo<<endl;
return 0;
}
This code will produce an error:
std::unique_ptr<_Tp, _Dp>::pointer = container*]: Assertion 'get() != pointer()' failed.
Hence telling us that we should probably include an if(ship1.upContainer) check.
What are good practice options for passing around objects in a program, avoiding accessing non initialized member variables.
Good practice would be to initialize everything in the constructor.
Debatable better practice is to initialize everything in the constructor and provide no way of modifying any members.
Preface:
this question is closely related to these ones: ...
- C++: Avoiding Static Initialization Order Problems and Race Conditions Simultaneously
- How to detect where a block of memory was allocated?
... but they have NO positive solution and my actual target use-case is slightly different.
During construction of the object I need to know if it is initialized in static memory bock ( BSS) or is it instantiated in Heap.
The reasons are follow:
Object by itself is designed to be initialized to "all zeros" in constructor - therefore no initialization is needed if object is statically initialized - entire block with all objects is already set to zeros when program is loaded.
Static instances of the object can be used by other statically allocated objects and alter some member variables of the object
Order of initialization of static variables is not pre-determined - i.e. my target object can be invoked before its constructor is invoked, thus altering some of its data, and constructor can be invoked later according to some unknown order of initialization of statics thus clearing already altered data. That is why I'd like to disable code in constructor for statically allocated objects.
Note: in some scenarios Object is the subject for severe multi-threaded access (it has some InterlockedIncrement/Decrement logic), and it has to be completely initialized before any thread can touch it - what i can guaranteed if i explicitly allocate it in Heep, but not in static area (but i need it for static objects too).
Sample piece of code to illustrate the case:
struct MyObject
{
long counter;
MyObject() {
if( !isStaticallyAllocated() ) {
counter = 0;
}
}
void startSomething() { InterlockedIncrement(&counter); }
void endSomething() { InterlockedDecrement(&counter); }
};
At the moment I'm trying to check if 'this' pointer in some predefined range, but this does not work reliably.
LONG_PTR STATIC_START = 0x00400000;
LONG_PTR STATIC_END = 0x02000000;
bool isStatic = (((LONG_PTR)this >= STATIC_START) && (LONG_PTR)this < STATIC_END));
Update:
sample use-case where explicit new operator is not applicable. Code is 'pseudo code', just to illustrate the use-case.
struct SyncObject() {
long counter;
SyncObject() {
if( !isStaticallyAllocated() ) {
counter = 0;
} }
void enter() { while( counter > 0 ) sleep(); counter++; }
void leave() { counter--; }
}
template <class TEnum>
struct ConstWrapper {
SyncObject syncObj;
TEnum m_value;
operator TEnum() const { return m_value; }
LPCTSTR getName() {
syncObj.enter();
if( !initialized ) {
loadNames();
intialized = true;
}
syncObj.leave();
return names[m_value];
}
}
ConstWrapper<MyEnum> MyEnumValue1(MyEnum::Value1);
You can probably achieve this by overwriting the new operator for your class. In your customized new, you can set a "magic byte" within the allocated memory, which you can later check for. This will not permit distinguishing stack from heap, but statically from dynamically allocated objects, which might be sufficient. Note, however, that in the following case
class A {
};
class B {
A a;
};
//...
B* b = new B;
b.a will be considered statically allocated with the proposed method.
Edit: A cleaner, but more complicated solution is probably a further customization of new, where you can keep track of dynamically allocated memory blocks.
Second edit: If you just want to forbid static allocation, why don't you just make the constructor private and add a factory function to the class dynamically creating the object and delivering the pointer?
class A {
private:
A () { ... }
public:
static A* Create () { return new A; }
};
I think that the best way for you to control this is to create a factory for your class. That way you have complete control of how your objects are created instead of making complicated guesses over what memory is used.
The first answer is: not portably, and it may not be possible at all on
some platforms. Under Solaris (and I think Linux as well), there is an
implicitly defined global symbol end, comparison of arbitrary
addresses works, and if this < &end (after the appropriate
conversions), the variable is static, at least as long as no dynamic
loading is involved. But this is far from general. (And it definitely
fails anytime dynamic linking is involved, regardless of the platform.)
The solution I've used in the past was to make the distinction manually.
Basically, I designed the class so that the normal constructor did the
same thing as zero initialization, and I then provided a special no-op
constructor for use with static objects:
class MayBeStatic
{
public:
enum ForStatic { isStatic };
MayBeStatic() { /* equivalent of zero initialization */ };
MayBeStatic( ForStatic ) { /* do absolutely nothing! */ };
// ...
};
When defining an instance with static lifetime, you use the second
constructor:
MayBeStatic object( MayBeStatic::isStatic );
I don't think that this is guaranteed by the standard; I think the
implementation is allowed to modify the memory any way it wants before
invoking the constructor, and in particular, I think it is allowed to
"redo" the zero initialization immediately before invoking the
constructor. None do, however, so you're probably safe in practice.
Alternatively, you can wrap all static instances in a function, so that
they are local statics, and will be initialized the first time the
function is called:
MayBeStatic&
getStaticInstance()
{
static MayBeStatic theInstance;
return theInstance;
}
Of course, you'll need a separate function for each static instance.
It looks like after thinking for a while, I've found a workable solution to identify if block is in static area or not. Let me know, please, if there are potential pitfalls.
Designed for MS Windows, which is my target platform - by another OS I actually meant another version of MS Windows: XP -> Win7. The idea is to get address space of the loaded module (.exe or .dll) and check if block is within this address space. Code which calculates start/end of static area is put into 'lib' segment thus it should be executed before all other static objects from 'user' segment, i.e. constructor can assume that staticStart/End variables are already initialized.
#include <psapi.h>
#pragma warning(push)
#pragma warning(disable: 4073)
#pragma init_seg(compiler)
#pragma warning(pop)
HANDLE gDllHandle = (HANDLE)-1;
LONG_PTR staticStart = 0;
LONG_PTR staticEnd = 0;
struct StaticAreaLocator {
StaticAreaLocator() {
if( gDllHandle == (HANDLE)-1 )
gDllHandle = GetModuleHandle(NULL);
MODULEINFO mi;
GetModuleInformation(GetCurrentProcess(), (HMODULE)gDllHandle, &mi, sizeof(mi));
staticStart = (LONG_PTR)mi.lpBaseOfDll;
staticEnd = (LONG_PTR)mi.lpBaseOfDll + mi.SizeOfImage;
// ASSERT will fail in DLL code if gDllHandle not initialized properly
LONG_PTR current_address;
#if _WIN64
ASSERT(FALSE) // to be adopted later
#else
__asm {
call _here
_here: pop eax ; eax now holds the [EIP]
mov [current_address], eax
}
#endif
ASSERT((staticStart <= current_address) && (current_address < staticEnd));
atexit(cleanup);
}
static void cleanup();
};
StaticAreaLocator* staticAreaLocator = new StaticAreaLocator();
void StaticAreaLocator::cleanup() {
delete staticAreaLocator;
staticAreaLocator = NULL;
}
I want to write the data "somebytes" that I get from a function called NextUnit() to a file named "output.txt", but the code that I wrote does not work. When I open the file, it does not look like my "somebytes". Here is the code:
#include <stdio.h>
#include <string.h>
char* NextUnit()
{
char Unit[256];
strcpy(Unit,"somebytes");
return &Unit[0];
}
int main()
{
FILE *ourfile;
ourfile=fopen("output.txt","wb");
char* somedata;
somedata=NextUnit();
printf("%s\n",somedata);
fwrite(somedata,1,strlen(somedata),ourfile);
fclose(ourfile);
}
You are returning the local address from a function (aka released stack address). Which is then changed once you call the next function.
Ether just return a global constant
const char* NextUnit() { return "somebytes"; }
or copy it into a new memory structure, which you will then need to also free later...
char* NextUnit()
{
char* newstr = new char[256];
strcpy(newstr,"somebytes");
return newstr;
}
// some code later
char* tmpstr = NextUnit();
// do writing stuff
// free memory
delete tmpstr;
You've declared Unit[256] on the stack in a subprocedure. But when your NextUnit() returns, the variable that was scoped to it goes out of scope, and you are no longer pointing to valid memory.
Consider allocating the memory with new, and then releasing it in the caller, or having the caller pass down a pointer to preallocated memory.
you are returning the local address of
a function. Ether just return
const char* NextUnit() { return
"somebytes"; }
so it's constant, or copy it into a
new memory stucture, which you will
then need to also free later...
I don't have enough mojo to comment on the quoted answer, so I have to put this as a new answer.
His answer is trying to say the right thing, but it came out wrong.
Your code is returning the address of a local variable inside the NextUnit() function. Don't do that. Its bad. Do what he suggested.
If you are using C++, the following is a much better way to go about this:
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char ** argv)
{
ofstream outFile;
outFile.open("output.txt");
outFile << "someBytes";
outFile.close();
return 0;
}
And, once you are comfortable with that, the next thing to learn about is RAII.
I would rewrite it like this:
char *NextUnit(char *src)
{
strcpy(src, "somebytes");
return src;
}
This way you can decide what to do with the variable outside the function implementation:
char Unit[256];
char *somedata = NextUnit(Unit);
NextUnit returns the address of Unit, which is an array local to that function. That means that it is allocated on the stack, and "released" when the function returns, making the return value non-valid.
To solve this problem you can:
Dynamically allocate a new string each time NextUnit is called. Note that in that case you will have to delete the memory afterwards.
Create a global string. That's fine for a small "test" application, but generally use of global variables is discouraged.
Have main allocate a string (either dynamically or on the stack), pass it as a parameter to NextUnit, and have NextUnit copy to that string.
You have a few problems here. The main one, I think, is that NextUnit() is allocating the buffer on the stack and you're effectively going out of scope when you try to return the address.
You can fix this in a C-style solution by mallocing space for the buffer and returning the pointer that malloc returns.
I think a first step might be to rewrite the code to something more like the following:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* NextUnit()
{
char *Unit = (char *)malloc( 256 );
memset(Unit, 0, sizeof(Unit));
strcpy(Unit,"somebytes");
return Unit;
}
int main()
{
FILE *ourfile;
ourfile=fopen("output.txt","wb");
char* somedata;
somedata=NextUnit();
printf("%s\n",somedata);
//fwrite(somedata,1,strlen(somedata),ourfile);
fprintf(ourfile, somedata);
free(somedata);
fclose(ourfile);
}
"Unit" declared as a local variable inside NextUnit which is actually a "stack" variable meaning that it's lifetime is only as long as NextUnit hasn't returned.
So, while NextUnit hasn't returned yet, copying "somebytes" to it is ok, as is printing it out. As soon as NextUnit returns, Unit is released from the stack and the pointer somedata in main will not be pointing to something valid.
Here is quick fix. I still don't recommend writing programs this way this way, but it's the least changes.
#include <stdio.h>
#include <string.h>
char Unit[256];
char* NextUnit()
{
strcpy(Unit,"somebytes");
return &Unit[0];
}
int main()
{
FILE *ourfile;
ourfile=fopen("output.txt","wb");
char* somedata;
somedata=NextUnit();
printf("%s\n",somedata);
fwrite(somedata,1,strlen(somedata),ourfile);
fclose(ourfile);
}
That works but it's kindof pointless returning the address of Unit when it's actually Global!
Declare Unit as static:
char* NextUnit()
{
static char Unit[256];
strcpy(Unit,"somebytes");
return &Unit[0];
}
But if you use C++ compiler you should consider using std::string instead of char*. std::string is more safe and will do all allocation/deallocation jobs for you.
I've read the article about scope guards (Generic: Change the Way You Write Exception-Safe Code — Forever) in DDJ and I understand their common use.
However, the common use is to instantiate a particular stack guard on the stack for a particular operation, e.g.:
{
FILE* topSecret = fopen("cia.txt");
ON_BLOCK_EXIT(std::fclose, topSecret);
... use topSecret ...
} // topSecret automagically closed
but what if I want to schedule cleanup operations in runtime, e.g. when I have a loop:
{
vector<FILE*> topSecretFiles;
for (int i=0; i<numberOfFiles; ++i)
{
char filename[256];
sprintf(filename, "cia%d.txt", i);
FILE* topSecret = fopen(filename);
topSecretFiles.push_back(topSecret);
ON_BLOCK_EXIT(std::fclose, topSecret); // no good
}
}
Obviously, the above example wouldn't work, since topSecret would be closed along with the for scope. I'd like a scope guard pattern where I can just as easily queue up cleanup operations which I determine to be needed at runtime. Is there something like this available?
I can't push scope guard objects into a standard queue, cause the original object (the one I'm pushing) would be dismissed in the process. How about pushing heap-allocated stack guards and using a queue which deletes its members on dtor? Does anyone have a more clever approach?
It seems you don't appreciate RAII for what it is. These scope guards are nice on occasion for local ("scope") things but you should try to avoid them in favour of what RAII is really supposed to do: encapsulating a resource in an object. The type FILE* is really just not good at that.
Here's an alternative:
void foo() {
typedef std::tr1::shared_ptr<FILE> file_sptr;
vector<file_sptr> bar;
for (...) {
file_sptr fsp ( std::fopen(...), std::fclose );
bar.push_back(fsp);
}
}
Or:
void foo() {
typedef std::tr1::shared_ptr<std::fstream> stream_sptr;
vector<stream_sptr> bar;
for (...) {
file_sptr fsp ( new std::fstream(...) );
bar.push_back(fsp);
}
}
Or in "C++0x" (upcoming C++ standard):
void foo() {
vector<std::fstream> bar;
for (...) {
// streams will become "movable"
bar.push_back( std::fstream(...) );
}
}
Edit: Since I like movable types in C++0x so much and you showed interest in it: Here's how you could use unique_ptr in combination with FILE* without any ref-counting overhead:
struct file_closer {
void operator()(FILE* f) const { if (f) std::fclose(f); }
};
typedef std::unique_ptr<FILE,file_closer> file_handle;
file_handle source() {
file_handle fh ( std::fopen(...) );
return fh;
}
int sink(file_handle fh) {
return std::fgetc( fh.get() );
}
int main() {
return sink( source() );
}
(untested)
Be sure to check out Dave's blog on efficient movable value types
Huh, turns out the DDJ scope guard is "movable", not in the C++0x sense, but in the same sense that an auto_ptr is movable: during the copy ctor, the new guard "dismisses" the old guard (like auto_ptr's copy ctor calls the old one's auto_ptr::release).
So I can simply keep a queue<ScopeGuard> and it'll work:
queue<ScopeGuard> scopeGuards;
// ...
for (...)
{
// the temporary scopeguard is being neutralized when copied into the queue,
// so it won't cause a double call of cleanupFunc
scopeGuards.push_back(MakeScopeGuard(cleanupFunc, arg1));
// ...
}
By the way, thank you for the answer above. It was informative and educational to me in different ways.