mercredi 4 septembre 2013

C++11: Combining lambda and smart pointer to handle create/release API

I use a library in which several class use create/release pattern for memory and resources management. it means that there is no public constructor (ctor) and destructor (dtor).

Example:

class FileInterface
{
public:
virtual ~FileInterface() {}

virtual bool isValid() = 0 ;
virtual void release() = 0;
};

class RealFile : public FileInterface
{
public:
static int createFileInterface(const std::string& filename, FileInterface*& pFileInst)
{
try {
pFileInst = new RealFile(filename);
} catch (...){
return -1;
}
return 0;
}
virtual bool isValid() { return (m_pFile != NULL);}
virtual void release() { delete this;}

protected:
RealFile(const std::string& filename)
: m_pFile(NULL)
{
m_pFile = fopen(filename.c_str(), "wb");
if(m_pFile == NULL) {
throw std::runtime_error("error while opening file.");
}
}
~RealFile() {
std::cout << "DTOR" << std::endl;
fclose(m_pFile);
}
private:
FILE* m_pFile;
};





To use that kind of class you have to deal with the release by yourself. it means that if you have several return path or a complex exception management, you have to put a call to the release function everywhere (with additional check for  not null).

FileInterface* pFile = nullptr;
int ret = RealFile::createFileInterface("test.bin", pFile);
if( ..... )
{
....
std::cout << "isValid = " << pFile->isValid() << std::endl;
pFile->release();
return ...;
}
else
{
....
if(pFile != nullptr)
pFile->release();
return ...;
}



That’s why I like the C++ smart pointer, at allocation you define the custom deleter and when the smart pointer instance goes out-of the scope, everything will be free.

auto smartDeleter = [](FileInterface* ptr){ptr->release();};
auto smartAllocator = [](const std::string& filename) -> FileInterface* {
FileInterface* pFile = nullptr;
int ret = RealFile::createFileInterface(filename, pFile);
if (ret != 0) return nullptr;
else return pFile;
};

std::unique_ptr<FileInterface, decltype(smartDeleter)> smartFile(smartAllocator("test.bin"));
std::cout << "isValid = " << smartFile->isValid() << std::endl;



Now we can use the smartFile instance as a pointer on a FileInterface and let the life management responsibility to the unique_ptr.

Aucun commentaire :

Enregistrer un commentaire