forked from rosh/MQL5Book
123 lines
3.6 KiB
MQL5
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[];
| |
//+------------------------------------------------------------------+
|