So I'm making a graphical application (game) that I want to utilize an Asset Manager. For this class I decided to use a Singleton Design. So in my main.cpp I would load something like...
ASSET_MANAGER.LoadImage("res/graphics/background.png", "background");
Here is the implication of the macros/methods used in the line above. This is sort of a mashup of code that I have to make things simpler to look at instead of pasting a few hundred lines of code into here.
assetmanager.h
#define ASSET_MANAGER AssetManager::GetAssetManager()
#define DEBUG
class AssetManager {
public:
static AssetManager &GetAssetManager();
//-----------------------------------------------------------------------------
// Purpose: Load a new image for the game to use. This function will store an
// instance of the asset in memory (in a hash map corresponding with
// the data type provided.
//
// param file: The location on disk of the asset
// param key: The string you use to receive this asset (defaults to the path str)
//-----------------------------------------------------------------------------
bool LoadImage(const char *file, const char *key = "");
//-----------------------------------------------------------------------------
// Purpose: Returns the image
//
// param key: The string used to store the asset in memory
//-----------------------------------------------------------------------------
ALLEGRO_BITMAP *GetImage(const char *key);
//-----------------------------------------------------------------------------
// Purpose: Destroys an asset that is presumably no longer needed by the game.
// This function is good for performance so that you don't use more
// RAM than you need to.
//
// param key: The string you use to receive this asset (defaults to the path str)
//-----------------------------------------------------------------------------
void DiscardImage(const char *key);
private:
AssetManager();
~AssetManager();
std::map<const char *, std::shared_ptr<ALLEGRO_BITMAP>> _ImageMap;
}
assetmanager.cpp
AssetManager &AssetManager::GetAssetManager() {
static AssetManager instance;
return instance;
}
bool AssetManager::LoadImage(const char *file, const char *key) {
key = key == "" ? file : key;
std::shared_ptr<ALLEGRO_BITMAP> x(al_load_bitmap(file), al_destroy_bitmap);
if (!x) {
fprintf(stderr, "Failed to load %s\n", file);
return false;
}
#ifdef DEBUG
printf("DEBUG: Loaded %s\n", key); //debug
#endif // DEBUG
_ImageMap.insert(std::pair<const char *, std::shared_ptr<ALLEGRO_BITMAP>>(key, x));
return true;
}
ALLEGRO_BITMAP *AssetManager::GetImage(const char *key) {
return _ImageMap.find(key) != _ImageMap.end() ? _ImageMap.at(key).get() : nullptr;
}
void AssetManager::DiscardImage(const char *key) {
_ImageMap.erase(key);
#ifdef DEBUG
printf("DEBUG: Discarded %s\n", key); //debug
#endif // DEBUG
}
This class only works from the class I initialized the asset manager in while I expected it to work anywhere that I called ASSET_MANAGER. It compiles fine, it only crashes when I try to use the manager in a different class and pass it into an allegro function because it returns something that is null instead of the proper allegro data types. What don't I understand about this?
The Char* in the map stores the location and not the data which makes the program think it's null.
Related
I'm learning some C++ with SDL and I've run into a confusing issue.
I have a class in a header file:
SDL_Surface* loadImage(const char*);
class GameSystem
{
public:
// Constructor
GameSystem();
// Destructor
~GameSystem();
SDL_PixelFormat* screenFormat();
private:
SDL_Window* m_window;
SDL_Surface* m_screen;
}
// Global pointer for all to access
extern const GameSystem* g_gameSystem;
In the constructor, m_window and m_screen are initialised fine. In the respective source file, I define this member function:
SDL_PixelFormat* GameSystem::screenFormat()
{
return m_screen->format;
}
The structure of m_screen is like this:
typedef struct SDL_Surface
{
Uint32 flags; /**< Read-only */
SDL_PixelFormat *format; /**< Read-only */
int w, h; /**< Read-only */
int pitch; /**< Read-only */
void *pixels;
}
To run this, in the main() function, I get an instance of GameSystem, and then point g_gameSystem from the header file to that instance. Like so:
int main(int argc, char** argv)
{
// Initialise the game system core
GameSystem gameSystem;
// Make the global gameSystem pointer point to this instance
g_gameSystem = &gameSystem;
My question is: how can I get the values of the format property to use the screenFormat function like this: g_gameSystem->screenFormat()
Many thanks in advance.
To answer your actual question, since I'm assuming that m_screen is properly initialized in your constructor:
g_gameSystem->screenFormat()->someVariable
Also, some unsolicited advice, do not use Hungarian notation.
I have a DLL written in C++ that wraps FindFirstFile/FindNextFile/FindClose to provide a file-search function:
std::vector<std::wstring> ELFindFilesInFolder(std::wstring folder, std::wstring fileMask = TEXT(""), bool fullPath = false);
This function returns a std::vector containing a list of filenames within the given folder matching the given filemask. So far so good; the function works as expected.
I need to write a C wrapper around this library, though, because I can't pass a vector across DLL boundaries. This is leading to no end of headaches.
I initially thought I would just set up a function that would receive a two-dimensional wchar_t array, modify it to contain the filename list, and return it:
bool ELFindFilesInFolder(const wchar_t* folderPath, const wchar_t* fileMask, const bool fullPath, wchar_t* filesBuffer[], size_t* filesBufferSize);
This proved to be a bad idea, however, as at least the second dimension's size has to be known at compile-time. I suppose I could just force the caller to make the second dimension MAX_PATH (so the function would receive a variable-length list of filename buffers, each MAX_PATH long), but this seems messy to me.
I considered a wrapper in the style of the Windows APIs:
bool ELFindNextFileInFolder(const wchar_t* folderPath, const wchar_t* fileMask, const bool fullPath, wchar_t* fileBuffer, size_t* fileBufferSize, HANDLE* searchToken);
This would perform the search, return the first filename found, and save the search handle provided by FindFirstFile. Future calls to ELFindNextFileInFolder would provide this search handle, making it easy to pick up where the last call left off: FindNextFile would just get the saved search handle. However, such handles are required to be closed via FindClose, and C doesn't seem to have the C++ concept of a smart pointer so I can't guarantee the searchToken will ever be closed. I can close some of the HANDLEs myself when FindNextFile indicates there are no more results, but if the caller abandons the search before that point there'll be a floating HANDLE left open. I'd very much like my library to be well-behaved and not leak HANDLEs everywhere, so this is out. I'd also prefer not to provide an ELCloseSearchHandle function, since I'm not sure I can trust callers to use it properly.
Is there a good, preferably single-function way to wrap these Windows APIs, or am I simply going to have to pick one from a list of imperfect solutions?
What about something like this?
In the DLL module:
#include <windows.h>
#include <vector>
#include <unordered_map>
unsigned int global_file_count; //just a counter..
std::unordered_map<unsigned int, std::vector<std::wstring>> global_file_holder; //holds vectors of strings for us.
/** Example file finder C++ code (not exported) **/
std::vector<std::wstring> Find_Files(std::wstring FileName)
{
std::vector<std::wstring> Result;
WIN32_FIND_DATAW hFound = {0};
HANDLE hFile = FindFirstFileW(FileName.c_str(), &hFound);
if (hFile != INVALID_HANDLE_VALUE)
{
do
{
Result.emplace_back(hFound.cFileName);
} while(FindNextFileW(hFile, &hFound));
}
FindClose(hFile);
return Result;
}
/** C Export **/
extern "C" __declspec(dllexport) unsigned int GetFindFiles(const wchar_t* FileName)
{
global_file_holder.insert(std::make_pair(++global_file_count, Find_Files(FileName)));
return global_file_count;
}
/** C Export **/
extern "C" __declspec(dllexport) int RemoveFindFiles(unsigned int handle)
{
auto it = global_file_holder.find(handle);
if (it != global_file_holder.end())
{
global_file_holder.erase(it);
return 1;
}
return 0;
}
/** C Export **/
extern "C" __declspec(dllexport) const wchar_t* File_Get(unsigned int handle, unsigned int index, unsigned int* len)
{
auto& ref = global_file_holder.find(handle)->second;
if (ref.size() > index)
{
*len = ref[index].size();
return ref[index].c_str();
}
*len = 0;
return nullptr;
}
/** C Export (really crappy lol.. maybe clear and reset is better) **/
extern "C" __declspec(dllexport) void File_ResetReferenceCount()
{
global_file_count = 0;
//global_file_holder.clear();
}
extern "C" __declspec(dllexport) bool __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, void* lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return true;
}
Then in the C code you can use it like:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
HMODULE module = LoadLibrary("CModule.dll");
if (module)
{
unsigned int (__cdecl *GetFindFiles)(const wchar_t* FileName) = (void*)GetProcAddress(module, "GetFindFiles");
int (__cdecl *RemoveFindFiles)(unsigned int handle) = (void*)GetProcAddress(module, "RemoveFindFiles");
const wchar_t* (__cdecl *File_Get)(unsigned int handle, unsigned int index, unsigned int* len) = (void*)GetProcAddress(module, "File_Get");
void (__cdecl *File_ResetReferenceCount)() = (void*)GetProcAddress(module, "File_ResetReferenceCount");
unsigned int index = 0, len = 0;
const wchar_t* file_name = NULL;
unsigned int handle = GetFindFiles(L"C:/Modules/*.dll"); //not an actual handle!
while((file_name = File_Get(handle, index++, &len)) != NULL)
{
if (len)
{
wprintf(L"%s\n", file_name);
}
}
RemoveFindFiles(handle); //Optional..
File_ResetReferenceCount(); //Optional..
/** The above two functions marked optional only need to be called
if you used FindFiles a LOT! Why? Because you'd be having a ton
of vectors not in use. Not calling it has no "leaks" or "bad side-effects".
Over time it may. (example is having 500+ (large) vectors of large strings) **/
FreeLibrary(module);
}
return 0;
}
It seems a bit dirty to be honest but I really don't know any "amazing" ways of doing it. This is just the way I do it.. Most of the work is done on the C++ side and you don't really have to worry about leaks.. Even exporting a function to clear the map would be nice too..
It would be better if the C exports were added to a template class and then export each of those.. That would make it re-useable for most C++ containers.. I think..
Change wchar_t* filesBuffer[] to wchar_t** *filesBuffer, then the caller can pass in a pointer to a wchar_t** variable to receive the array and does not need to know anything about any bounds at compile time. As for the array itself, the DLL can allocate a one-dimensional array of wchar_t* pointers that point to null-terminated strings. That way, your size_t* filesBufferSize parameter is still relevant - it receives the number of strings in the array.
bool ELFindFilesInFolder(const wchar_t* folderPath, const wchar_t* fileMask, const bool fullPath, wchar_t** *filesBuffer, size_t* filesBufferSize);
wchar_t **files;
size_t numFiles;
if (ELFindFilesInFolder(..., &files, &numFiles))
{
for(size_t i = 0; i < numFiles; ++i)
{
// use files[i] as needed ...
}
// pass files back to DLL to be freed ...
}
Another option is to do something similar to WM_DROPFILES does. Have ELFindFilesInFolder() return an opaque pointer to an internal list, and then expose a separate function that can retrieve a filename at a given index within that list.
bool ELFindFilesInFolder(const wchar_t* folderPath, const wchar_t* fileMask, const bool fullPath, void** filesBuffer, size_t* filesBufferSize);
bool ELGetFile(const wchar_t* fileName, size_t fileNameSize, void* filesBuffer, size_t fileIndex);
void *files;
size_t numFiles;
wchar_t fileName[MAX_PATH + 1];
if (ELFindFilesInFolder(..., &files, &numFiles))
{
for(size_t i = 0; i < numFiles; ++i)
{
ELGetFile(fileName, MAX_PATH, files, i);
// use fileName as needed ...
}
// pass files back to DLL to be freed ...
}
Any way you do it, the DLL has to manage the memory, so you have to pass some kind of state info to the caller and then have that passed back to the DLL for freeing. There is no many ways around that in C, unless the DLL keeps track of the state info internally (but then you have to worry about thread safety, reentrancy, etc) and frees it after the last file is retrieved. But that would require the caller to reach the last file, whereas the other approaches allow the caller to finish earlier if desired.
I'm trying to program a video buffer in a std::deque in my OFX video plug-in. I would like to access previously processed images in order to process the current image. My idea is to push processed images to the front of the deque and pop them from the back if the buffer exceeds the maximum size.
The plug-in crashes when I try to free the memory of an image using delete before removing it from the buffer. I found out that I can add one or several images to the buffer and delete and remove them immediately afterwards with no problem. However, if I try to delete an image which has been added in an earlier cycle, it crashes.
The plug-in consists of the main class OFXPlugin and the processor class myplugin. The instance of OFXPlugin stays over time, but for every image to be processed it creates an instance of myplugin and destroys it after processing that frame.
I'm not sure if I'm doing something wrong in the way I use the deque, if I'm not allowed to free memory which has been allocated by another instance of myplugin or if I'm doing something illegal related to the OFX API.
The Code below shows the extracts of the plug-in related to the problem. It's based on the OFX Support examples. It crashes at delete videoBuffer_.back().img; in the function OFXPlugin::addToVBuff(OFX::Image *img, double t). I cannot catch an exception, apparently it is handled (ignored) in the OFX API.
Thanks a lot for your help!
myplugin.h
#include "ofxsImageEffect.h"
#include "ofxsMultiThread.h"
#include "../Support/Plugins/include/ofxsProcessing.H"
#include <deque>
// Video Buffer Element
typedef struct vBuffEl
{
OFX::Image* img;
double time;
} vBuffEl;
inline
bool operator==(const vBuffEl &a, const double b)
{
return a.time == b;
}
class myplugin : public OFX::ImageProcessor {
protected :
OFX::Image *_srcImg;
double _time;
OFXPlugin *_opInstance;
public :
// ctor
myplugin(OFX::ImageEffect &instance)
: OFX::ImageProcessor(instance)
, _srcImg(0)
, _time(0)
{}
void multiThreadProcessImages(OfxRectI procWindow);
void setOFXPlugin(OFXPlugin* opInstance) {_opInstance = opInstance;}
OFXPlugin* getOFXPlugin() {return _opInstance;}
void setTime(double argsTime) {_time = argsTime;}
double getTime() {return _time;}
void setSrcImg(OFX::Image *v) {_srcImg = v;}
OFX::Image* getSrcImg() {return _srcImg;}
};
class OFXPlugin : public OFX::ImageEffect {
protected :
OFX::Clip *dstClip_;
OFX::Clip *srcClip_;
double time_;
std::deque<vBuffEl> videoBuffer_;
public :
/** #brief ctor */
OFXPlugin(OfxImageEffectHandle handle);
/** #brief dtor */
~OFXPlugin();
/* Override the render */
virtual void render(const OFX::RenderArguments &args);
/* get the source Clip */
OFX::Clip* getSrcClip();
/* get the current time */
double getTime();
/* set up and run a processor */
void setupAndProcess(myplugin &, const OFX::RenderArguments &args);
/* add to video buffer */
void addToVBuff(OFX::Image *img, double t);
/* fetch a dst image from buffer */
void fetchDstImageBuff(double t, OFX::Image* &img, bool &buff);
};
myplugin.cpp
#include "myplugin.h"
#include <algorithm>
void myplugin::multiThreadProcessImages(OfxRectI procWindow)
{
// Do some filtering of the source image and store result in destination image
myfiltering(_dstImg, _srcImg, procWindow);
// add to buffer
_opInstance->addToVBuff(_dstImg, _time);
}
/* set up and run a processor */
void
OFXPlugin::setupAndProcess(myplugin &processor, const OFX::RenderArguments &args)
{
// get a dst image
std::auto_ptr<OFX::Image> dst(dstClip_->fetchImage(args.time));
OFX::BitDepthEnum dstBitDepth = dst->getPixelDepth();
OFX::PixelComponentEnum dstComponents = dst->getPixelComponents();
// fetch main input image
std::auto_ptr<OFX::Image> src(srcClip_->fetchImage(args.time));
// set the images
processor.setDstImg(dst.get());
processor.setSrcImg(src.get());
// set the render window
processor.setRenderWindow(args.renderWindow);
// set time
processor.setTime(args.time);
time_ = args.time;
// set OFXPlugin instance
processor.setOFXPlugin(this);
// Call the base class process member, this will call the derived templated process code
processor.process();
}
OFX::Clip* OFXPlugin::getSrcClip()
{
return srcClip_;
}
/* get the current time */
double
OFXPlugin::getTime()
{
return time_;
}
// the overridden render function
void
OFXPlugin::render(const OFX::RenderArguments &args)
{
try {
myplugin fred(*this);
setupAndProcess(fred, args);
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
/* add to video buffer */
void
OFXPlugin::addToVBuff(OFX::Image *img, double t)
{
try {
// if frame already exists in buffer, remove
std::deque<vBuffEl>::iterator it;
it = find(videoBuffer_.begin(), videoBuffer_.end(), t);
if(it != videoBuffer_.end())
{
delete it->img;
videoBuffer_.erase(it);
}
// add new frame to the front
vBuffEl e;
e.time = t;
e.img = new OFX::Image(img->getPropertySet().propSetHandle());
memcpy(e.img, img, sizeof(img));
videoBuffer_.push_front(e);
// remove elements at the end, if the buffer exceeds the max size
int LASTIMG_ARRAY_SIZE = 10;
while(videoBuffer_.size() > LASTIMG_ARRAY_SIZE)
{
delete videoBuffer_.back().img;
videoBuffer_.erase(--(videoBuffer_.end()));
}
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
/* fetch a dst image from buffer */
void
OFXPlugin::fetchDstImageBuff(double t, OFX::Image* &img, bool &buff)
{
try {
std::deque<vBuffEl>::iterator it;
it = find(videoBuffer_.begin(), videoBuffer_.end(), t);
if(it != videoBuffer_.end())
{
img = it->img; // return buffered dst image
buff = true;
}
else
{
img = getSrcClip()->fetchImage(t); // fetch and return src image
buff = false;
}
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
The statement
memcpy(e.img, img, sizeof(img));
doesn't do what you expect it to.
The sizeof operation of a pointer returns the size of the pointer, not what it points to. This means that in this case, you are only copying 4 or 8 bytes (depending on if you are on a 32 or 64 bit platform).
However, there is another worse problem hidden in that memcpy call. If the OFX::Image contains data member pointers, copying the data will copy the pointers and not the data. It's a shallow copy, not a deep copy. This is a reason C++ has copy constructors and copy assignment operators.
What you should to is a simple assignment, and hope that OFX::Image follows the rule of three:
*e.img = *img;
I've been tasked with putting a C# wrapper around an old C++ dll that has had the source mislaid. I do have some details of the dll:
enum DataItemType {DataItemType_String, DataItemType_Number, DataItemType_Date};
typedef struct Data_Item_Node_
{
char *field_name;
char *field_value;
enum DataItemType field_type;
struct Data_Item_Node_ *next;
} Data_Item_Node;
typedef struct Data_Item_List_
{
Data_Item_Node *first; // points to first item in the list
Data_Item_Node *last; // points to last item in the list
long count; // number of data items in the list
Data_Item_Node **index; // sorted array of pointers to the list items
char *pDataDumpBuffer; // pointer to a buffer used by the [*DumpData] command
} Data_Item_List;
extern "C" SAIL_IMP_EXP BOOL WINAPI Sail_Validate(const char *sail, const char *streams, const char *inserts)
extern "C" SAIL_IMP_EXP BOOL WINAPI Sail_GetStreamAndInserts(char *sail, Data_Item_List_ *data, char *stream, char inserts[][9], int insert_count)
extern "C" SAIL_IMP_EXP LONG WINAPI Sail_GetErrorMessage(LPSTR error, DWORD len)
As you can see, there are 3 methods I need to call. I've created a C# command line wrapper to test it with and used DLLImport to reference the exposed methods:
class Program
{
[DllImport("Sail_.dll")]
public static extern bool Sail_Validate(string sail, string streams, string inserts);
static void Main(string[] args)
{
string p_sail = args[0];
string p_streams = args[1];
string p_inserts = args[2];
try
{
bool Result = Sail_Validate(p_sail, p_streams, p_inserts);
Console.WriteLine("Result: " + Result.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
}
This C# code seems to work so far for the one method it calls, but I've hit a bit of a brick wall at the next one 'Sail_GetStreamAndInserts', mainly with the Data_Item_List_ & 2 dimensional array parameters.
I'd be grateful for any help in how to define the parameters for the remaining methods.
For our C/C++ app we are using Security Transforms API for doing some basic encryption/decryption.
And now we need to calculate hash (especially SHA256) of data, and though documentation claims that Security Transforms also provides a way of hashing, but seems there is no details of how to do it. And seems google doesn't bring any example or details on it as well.
So question is:
Is it really possible to calculate hash (SHA256 if possible) using Security Transforms?
And if no, then is there any other API (provided by Apple) to calculate it using C/C++?
I don't know about Security Transforms. You can use Apple's CommonCrypto library for this, though.
Oddly much of CommonCrypto does not seem to be well documented (at least that I can find), but in https://opensource.apple.com//source/CommonCrypto/CommonCrypto-7/CommonCrypto/CommonDigest.h.auto.html find the following declarations:
extern int CC_SHA256_Init(CC_SHA256_CTX *c);
extern int CC_SHA256_Update(CC_SHA256_CTX *c, const void *data, CC_LONG len);
extern int CC_SHA256_Final(unsigned char *md, CC_SHA256_CTX *c);
After digging a lot it turned out that it's possible with Security Transforms API though it was not documented. To make it work, crafted samples of AES encryption with SecDigestTransformCreate and used list of available hashing algorithms there.
Here is a C and C++ friendly solution:
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#ifdef __cplusplus
#include <vector>
#else // C
#include <stdbool.h> // For adding boolean support
#endif // __cplusplus
// Convenience define for cleanup
#define _CLEANUP_IF(a) if ((a)) goto Cleanup;
#ifdef __cplusplus
// Wrap into class in case of C++
class Sha256Calculator {
public:
#endif // __cplusplus
// Calculates SHA256 hash from given array of data and returns array
// Note: Parameter "outHash" is manually allocated so consider calling free(outHash) after using it
static bool calculateSha256(uint8_t** outHash, size_t* outHashSize, const uint8_t *data, const size_t dataSize)
{
bool result = false;
CFErrorRef error = NULL;
SecTransformRef digestTransform = NULL;
CFDataRef sourceData = NULL;
CFDataRef outDataRef = NULL;
const UInt8 * outData = NULL;
CFIndex outDataSize = 0;
// Create a CFData object from the source
sourceData = CFDataCreate(kCFAllocatorDefault, (const UInt8*)data, dataSize);
_CLEANUP_IF(!sourceData);
digestTransform = SecDigestTransformCreate(kSecDigestSHA2, 256, &error);
_CLEANUP_IF(error);
SecTransformSetAttribute(digestTransform, kSecTransformInputAttributeName, (CFDataRef)sourceData, &error);
_CLEANUP_IF(error);
outDataRef = (CFDataRef)SecTransformExecute(digestTransform, &error);
_CLEANUP_IF(error);
_CLEANUP_IF(!outDataRef);
// Extract data from CFDataRef to array
outData = CFDataGetBytePtr(outDataRef); // Returns read-only (UInt8*) pointer to the data
outDataSize = CFDataGetLength(outDataRef);
if (outHash) {
*outHash = (uint8_t*)malloc(outDataSize);
if (*outHash) {
memcpy(*outHash, outData, outDataSize);
if (outHashSize) {
*outHashSize = (size_t)outDataSize;
}
result = true;
}
}
// Notes:
// * All the objects are released except "outData" since it's handled and cleaned by using outDataRef
// * CFRelease throws error if the passed object is NULL, so check objects before releasing
Cleanup:
// Use CFShow(error) for getting details about error
if (error) { CFRelease(error); }
if (digestTransform) { CFRelease(digestTransform); }
if (sourceData) { CFRelease(sourceData); }
if (outDataRef) { CFRelease(outDataRef); }
return result;
}
#ifdef __cplusplus
// Convenience method for cpp using vectors
static bool calculateSha256(std::vector<uint8_t>& outHash, const std::vector<uint8_t>& data)
{
// Call original method
uint8_t * outHashArray = nullptr;
size_t outHashSize;
bool result;
result = calculateSha256(&outHashArray, &outHashSize, data.data(), data.size());
if (!result)
return false;
// Put resulting array in vector
outHash.clear();
outHash.insert(outHash.end(), &outHashArray[0], &outHashArray[outHashSize]);
// Clean allocated array
if (outHashArray)
free(outHashArray);
return result;
}
};
#endif // __cplusplus
Note:
In order to use any other hashing algorithgm instead of SHA256 feel free to modify the line:
SecDigestTransformCreate(kSecDigestSHA2, 256, &error);
with desired available hashing algorithm name and appropriate length.
PS Hope, Apple will guys will update their documentation...