MQL5Book/Include/FileHolder.mqh
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

123 lines
3.6 KiB
MQL5

//+------------------------------------------------------------------+
//| FileHolder.mqh |
//| Copyright 2021, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include "MqlError.mqh"
//+------------------------------------------------------------------+
//| Base auxiliry class just to clean up internal error flags, |
//| which may remain after previous function calls |
//+------------------------------------------------------------------+
class FileBase
{
protected:
FileBase()
{
ResetLastError();
}
};
//+------------------------------------------------------------------+
//| Single file-handle guard to prevent resource leak, |
//| closes file automatically on object deletion |
//+------------------------------------------------------------------+
class FileOpener : public FileBase
{
public:
const int handle;
const string name;
FileOpener(const string filename, const int flags, const ushort delimiter = '\t', const uint codepage = CP_ACP) :
handle(FileOpen(filename, flags, delimiter, codepage)), name(filename)
{
}
~FileOpener()
{
ResetLastError();
FileGetInteger(handle, FILE_SIZE); // marks internal error if handle is incorrect
if(handle != INVALID_HANDLE)
{
if(_LastError == 0)
{
#ifdef FILE_DEBUG_PRINT
Print(__FUNCTION__, ": Automatic close for handle: ", handle);
#endif
FileClose(handle);
}
else
{
PrintFormat("%s: handle %d is incorrect, %s(%d)",
__FUNCTION__, handle, E2S(_LastError), _LastError);
}
}
}
};
#define CONTEXT_MARK NULL
#define CLEAR(P) if(CheckPointer(P) == POINTER_DYNAMIC) delete P;
//+------------------------------------------------------------------+
//| Multiple file-handle guard to prevent resource leak, |
//| generates FileOpener objects upon requests, then delete them |
//| automatically on leaving local contexts |
//+------------------------------------------------------------------+
class FileHolder
{
static FileOpener *files[];
int expand()
{
return ArrayResize(files, ArraySize(files) + 1) - 1;
}
public:
FileHolder()
{
const int n = expand();
if(n > -1)
{
files[n] = CONTEXT_MARK;
}
}
~FileHolder()
{
for(int i = ArraySize(files) - 1; i >= 0; --i)
{
if(files[i] == CONTEXT_MARK)
{
// shrink array and exit
ArrayResize(files, i);
return;
}
CLEAR(files[i]);
}
}
int FileOpen(const string filename, const int flags, const ushort delimiter = '\t', const uint codepage = CP_ACP)
{
const int n = expand();
if(n > -1)
{
files[n] = new FileOpener(filename, flags, delimiter, codepage);
return files[n].handle;
}
return INVALID_HANDLE;
}
static string GetFilename(const int h)
{
for(int i = 0; i < ArraySize(files); ++i)
{
if(files[i] != NULL && files[i].handle == h)
{
return files[i].name;
}
}
return NULL;
}
};
static FileOpener *FileHolder::files[];
//+------------------------------------------------------------------+