I have several member functions inside my C++ class which i packaged them as a dll and tried to used them in a C# application.
I tried to create two async functions by simply executing them using threads and then detach()ing them so that they wouldn't block the caller thread till they end.
In C++ based application whenever i use threads to call functions this way, they work, but when i tried to call one of my async functions from C#,They seem to execute but there is no result.
These two async functions are supposed to create text files upon finishing their job, But they dont while their ordinary counter parts do!
I'll post snippets of my class definition and also my C wrapper (for the dll ) and my c# application section.
//xGramManipulator.h file
#pragma once
#include <regex>
#include <string>
#include <map>
#include <thread>
#include <mutex>
#include <vector>
class xGramManipulator
{
public:
xGramManipulator();
xGramManipulator(wchar_t* fileToRead);
xGramManipulator(std::wregex monoReg, std::wregex biReg, wchar_t* fileToRead = L"ftext.txt");
~xGramManipulator();
double CreateMonoGram();
double ReadMonoGram();
void SaveMonoGram();
double CreateBiGram();
double ReadBiGram();
void SaveBiGram();
double CreateLists();
void CreateMonoGramAsync();
void CreateBiGramAsync();
priavte:
template<class TypeOne, class TypeTwo>
void WriteToFile(const wchar_t* filePath, std::map<TypeOne, TypeTwo>& container);
private:
std::map<std::wstring, int> monogramStatMap;
std::map<std::wstring, int> bigramStatMap;
std::map<std::wstring, std::wstring> bigramListMap;
std::vector<std::wstring> nextWordResults;
std::unordered_multimap< std::wstring, std::wstring > container;
std::mutex mutex_;
wchar_t* fileToReadFrom;
wchar_t **CStringArraybuffer;
std::wregex monoRegx;
std::wregex biRegx;
std::thread tr1;
std::thread tr2;
std::chrono::high_resolution_clock::time_point startTime;
std::chrono::high_resolution_clock::time_point endTime;
std::chrono::high_resolution_clock::time_point elapsedTime;
};
template <class TypeOne, class TypeTwo>
void xGramManipulator::WriteToFile(const wchar_t* filePath, std::map<TypeOne, TypeTwo>& container)
{
//Because of possible use of threads and inorder to avoid any thread interferance
//i decided to declare it local instead of global.
FILE* filePointer;
//for converting To UTF8 so that our contents are in farsi and not some numeric nonsense!
_wfopen_s(&filePointer, filePath, L"w");
_setmode(_fileno(filePointer), _O_U8TEXT);
wofstream output(filePointer);
for (auto item : container)
{
//write output to file
output << item.first << " : " << item.second << endl;
}
fclose(filePointer);
}
//xGramManipulator.cpp file
#include <regex>
#include <fstream>
#include <iostream>
#include <string>
#include <map>
#include <fcntl.h> //for _wfopen_s
#include <io.h> //for _setmode
#include <memory> //for shared_ptr
#include <chrono> //for time benchmarks
#include <thread>
#include <mutex>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include "xGramManipulator.h"
using namespace std;
using namespace chrono;
typedef high_resolution_clock Time;
xGramManipulator::xGramManipulator() : monoRegx(L"\\w+"), biRegx(L"\\w+ \\w+"), fileToReadFrom(L"ftext.txt")
{}
xGramManipulator::xGramManipulator(wchar_t* fileToRead) : monoRegx(L"\\w+"), biRegx(L"\\w+ \\w+"), fileToReadFrom(fileToRead)
{
if (!FileExits(fileToReadFrom))
{
cout << "File doesnt Exists" << endl;
}
}
xGramManipulator::xGramManipulator(wregex monoReg, wregex biReg, wchar_t* fileToRead) : monoRegx(monoReg), biRegx(biReg), fileToReadFrom(fileToRead)
{
if (!FileExits(fileToReadFrom))
{
cout << "File doesnt Exists";
}
}
xGramManipulator::~xGramManipulator()
{
}
//Reads from a file specified in the constructor or through SetFilePathToReadFrom() function
//And then creates a list of monograms.When function execution ends, the elapsed time is returned.
double xGramManipulator::CreateMonoGram()
{
Time::time_point start = Time::now();
ReadMonoGram();
SaveMonoGram();
Time::time_point end = Time::now();
return ElapsedTime(start, end);
}
//Reads from a file specified in the constructor or through SetFilePathToReadFrom() function
//And then creates a list of bigrams.When function execution ends, the elapsed time is returned.
double xGramManipulator::CreateBiGram()
{
Time::time_point start = Time::now();
ReadBiGram();
SaveBiGram();
Time::time_point end = Time::now();
return ElapsedTime(start, end);
}
double xGramManipulator::ReadMonoGram()
{
FILE* filePointer;
wstring inputString = L"";
Time::time_point start = Time::now();
try
{
//converting UTF16 to UTF8 so that we dont get rubbish result!
_wfopen_s(&filePointer, fileToReadFrom, L"r");
_setmode(_fileno(filePointer), _O_U8TEXT);
wifstream file(filePointer);
monogramStatMap.clear();
while (file.good())
{
getline(file, inputString);
for (wsregex_iterator it(inputString.begin(), inputString.end(), monoRegx), it_end; it != it_end; ++it)
{
//Filling the bigram container
//unorderedmap estrefade beshe baraye soorate bishtar
monogramStatMap[(wstring) (*it)[0]]++;
}
}
fclose(filePointer);
}
catch (exception ex)
{
cout << ex.what() << endl;
}
Time::time_point end = Time::now();
return ElapsedTime(start, end);
}
void xGramManipulator::SaveMonoGram()
{
WriteToFile(L"monogramLists.txt", monogramStatMap);
}
double xGramManipulator::ReadBiGram()
{
FILE* filePointer;
wstring inputString = L"";
wstring wstr = L"";
Time::time_point start = Time::now();
try
{
//converting UTF16 to UTF8 so that we dont get rubbish result!
_wfopen_s(&filePointer, fileToReadFrom, L"r");
_setmode(_fileno(filePointer), _O_U8TEXT);
wifstream file(filePointer);
bigramStatMap.clear();
container.clear();
while (file.good())
{
getline(file, inputString);
for (wsregex_iterator it(inputString.begin(), inputString.end(), biRegx), it_end; it != it_end; ++it)
{
//Filling the bigram container
wstr = (wstring) (*it)[0];
bigramStatMap[wstr]++;
//spliting the two words into two items.
bigramListMap[wstr.substr(0, wstr.find(L' '))] = wstr.substr(wstr.find(L' '), wstr.size());
container.emplace(wstr.substr(0, wstr.find(L' ')), wstr.substr(wstr.find(L' '), wstr.size()));
}
}
fclose(filePointer);
}
catch (exception ex)
{
cout << ex.what() << endl;
}
Time::time_point end = Time::now();
return ElapsedTime(start, end);
}
void xGramManipulator::SaveBiGram()
{
WriteToFile(L"bigramStatList.txt", bigramStatMap);
WriteToFile(L"bigramList.txt", bigramListMap);
}
double xGramManipulator::CreateLists()
{
startTime = Time::now();
tr1 = thread(&xGramManipulator::CreateMonoGram, this);
tr2 = thread(&xGramManipulator::CreateBiGram, this);
tr1.join();
tr2.join();
endTime = Time::now();
return ElapsedTime(startTime, endTime);
}
void xGramManipulator::CreateMonoGramAsync()
{
thread t(&xGramManipulator::ReadMonoGram, this);
t.detach();
}
void xGramManipulator::CreateBiGramAsync()
{
//static_cast<void (xGramManipulator::*)()>(&xGramManipulator::ReadMonoGram)
thread t = thread(&xGramManipulator::ReadBiGram, this);
t.detach();
}
And this is my Dll definitions : //CDll.h
#ifdef CDLL_EXPORTS
#define CDLL_API __declspec(dllexport)
#else
#define CDLL_API __declspec(dllimport)
#endif
extern "C"
{
CDLL_API double CreateMonoAndBioGramLists(void); //reads and creates two files for each
CDLL_API double CreateMonoGram(void); //reads and creates a file
CDLL_API double CreateBiGram(void); //reads and creates a file
CDLL_API wchar_t** GetResults(wchar_t* word , int threshold, int* lemgth); //returns the result of the search
CDLL_API double ReadMonoGram(void);
CDLL_API double ReadBiGram(void);
CDLL_API void CreateMonoGramAsync(void);
CDLL_API void CreateBiGramAsync(void);
CDLL_API void* CreateHandle();
CDLL_API void* GetCurrentHandle();
CDLL_API void DisposeCurrentHandle();
CDLL_API void SetCurrentHandle(void* handle);
CDLL_API void* GetHandle();
CDLL_API void DisposeHandle(void*);
}
//CDLL.cpp
#include "CDll.h"
#include "xGramManipulator.h"
#define DLL_EXPORT
extern "C"
{
xGramManipulator *xG;
CDLL_API double CreateMonoAndBioGramLists(void)
{
//return xG.CreateLists();
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->CreateLists();
}
CDLL_API double CreateMonoGram(void)
{
//return xG.CreateMonoGram();
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->CreateMonoGram();
}
CDLL_API double CreateBiGram(void)
{
//return xG.CreateBiGram();
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->CreateBiGram();
}
CDLL_API wchar_t** GetResults(wchar_t* word, int threshold, int* length)
{
//return xG.CGetNextWordsList(word, length, threshold);
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->CGetNextWordsList(word, length, threshold);
}
CDLL_API double ReadMonoGram(void)
{
//return xG.ReadMonoGram();
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->ReadMonoGram();
}
CDLL_API double ReadBiGram(void)
{
//return xG.ReadBiGram();
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->ReadBiGram();
}
CDLL_API void CreateMonoGramAsync(void)
{
//xG.CreateMonoGramAsync();
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->CreateMonoGramAsync();
}
CDLL_API void CreateBiGramAsync(void)
{
//xG.CreateBiGramAsync();
xG = reinterpret_cast<xGramManipulator*>(GetHandle());
return xG->CreateBiGramAsync();
}
//Handle operations
CDLL_API void* CreateHandle()
{
if (xG == nullptr)
{
xG = new xGramManipulator;
}
else
{
delete xG;
xG = new xGramManipulator;
}
return reinterpret_cast<void*>(xG);
}
CDLL_API void* GetCurrentHandle()
{
return reinterpret_cast<void*>(xG);
}
CDLL_API void DisposeCurrentHandle()
{
delete xG;
xG = nullptr;
}
CDLL_API void SetCurrentHandle(void* handle)
{
if (handle != nullptr)
{
xG = reinterpret_cast<xGramManipulator*>(handle);
}
else
{
xG = new xGramManipulator;
}
}
//factory utility function
//ask question on how to do it.
CDLL_API void* GetHandle()
{
HANDLE handle = GetCurrentHandle();
if (handle != nullptr)
{
return handle;
}
else
{
xG = new xGramManipulator;
handle = reinterpret_cast <void*>(xG);
}
return handle;
}
CDLL_API void DisposeHandle(void* handle)
{
xGramManipulator * tmp = reinterpret_cast<xGramManipulator*>(handle);
delete tmp;
}
Notice:
At first instead of
xGramManipulator *xG;
I had
xGramManipulator xG;
and i simply used that global object to call member functions.after some thought i thought maybe this is'nt good and i should somehow make it dynamic!? so i changed it to the current state.
Please guide me if the former solution (using global object xG) was right or wrong and if wrong what kind of problems i would face?
If the current approch is wrong then what should i do ?