Trying to replace delete pointer call with std::unique_ptr call - visual-c++

I am getting myself confused again with unique pointers and deletion.
I am trying to avoid the need for the delete pWidth line below:
void CCreateReportDlg::DeleteColumnWidthMap(CMapWordToPtr& rMapColumnWidth)
{
WORD wColumn{}, * pWidth = nullptr;
auto sPos = rMapColumnWidth.GetStartPosition();
while (sPos != nullptr)
{
rMapColumnWidth.GetNextAssoc(sPos, wColumn, reinterpret_cast<void*&>(pWidth));
if (pWidth != nullptr)
{
//delete pWidth;
std::unique_ptr<WORD*> cleanup(pWidth);
}
}
rMapColumnWidth.RemoveAll();
}
Why is it saying this?
Ah, I think I now see that the unique_ptr is supposed to be an array of WORD pointers. But I only have just the one value.
Update
Example of how I populate the map (parameter to function):
void CCreateReportDlg::HideColumns(CGridCtrl* pGrid, const CDWordArray* pAryDWColumns, CMapWordToPtr& rMapColumnWidth)
{
ASSERT(pGrid != nullptr);
ASSERT(pAryDWColumns != nullptr);
if (pGrid == nullptr || pAryDWColumns == nullptr)
return;
DeleteColumnWidthMap(rMapColumnWidth);
const auto iSize = pAryDWColumns->GetSize();
for (INT_PTR i = 0; i < iSize; i++)
{
const auto dwColumnData = pAryDWColumns->GetAt(i);
const auto iCol = LOWORD(dwColumnData);
const auto eImage = static_cast<CheckImageIndex>(HIWORD(dwColumnData));
if (eImage == CheckImageIndex::Unchecked)
{
auto pWidth = std::make_unique<WORD>().release();
ASSERT(pWidth != nullptr);
if (pWidth != nullptr)
{
*pWidth = pGrid->GetColumnWidth(iCol);
rMapColumnWidth.SetAt(iCol, pWidth);
}
pGrid->SetColumnWidth(iCol, 0);
}
}
}

Use {std::unique_ptr<WORD> cleanup(pWidth);} for deleting pWidth = std::make_unique<WORD>().release();
But std::map<int,int> (or unordered_map) is a much better than CMapWordToPtr, you don't need to store a pointer here.
You should be able to simplify your function like this:
void HideColumns(CGridCtrl* pGrid,
std::vector<DWORD> &pAryDWColumns, std::map<int,int> &rMapColumnWidth)
{
//clear the map, it doesn't need separate function
rMapColumnWidth.clear();
for (auto dword : pAryDWColumns)
{
const auto iCol = LOWORD(dword);
const auto eImage = static_cast<CheckImageIndex>(HIWORD(dword));
if (eImage == CheckImageIndex::Unchecked)
{
rMapColumnWidth[iCol] = pGrid->GetColumnWidth(iCol);
pGrid->SetColumnWidth(iCol, 0);
}
pGrid->SetColumnWidth(iCol, 0);
}
}
...
for (auto e : map)
TRACE("%d\n", e);
By the way, in another question I think I recommended using std::unique_ptr to turn off some Code Analysis messages. You should ignore that advice. Either stick with new/delete or use STL classes which have automatic memory management.
You can still use std::unique_ptr in some special cases, for example when passing data to APIs or some MFC functions.

Related

Will it be simpler to convert this code from CStringArray to std::vector<CString>?

Given this code:
void CSelectNamesDlg::ShuffleArray(CString strName, CStringArray *pAryStrNames)
{
if (pAryStrNames == nullptr)
return;
const auto iSize = pAryStrNames->GetSize();
if (iSize > 1)
{
// First, we must locate strName in the array
auto i = CSelectNamesDlg::LocateText(strName, pAryStrNames);
if (i != -1)
{
const auto iName = i;
// We must now shuffle the names from the bottom to the top
const auto iCount = gsl::narrow<int>(iSize) - iName;
for (i = 0; i < iCount; i++)
{
CString strTemp = pAryStrNames->GetAt(iSize-1);
pAryStrNames->RemoveAt(iSize-1);
pAryStrNames->InsertAt(0, strTemp);
}
}
}
}
int CSelectNamesDlg::LocateText(CString strText, const CStringArray *pAryStrText)
{
bool bFound = false;
int i{};
if (pAryStrText != nullptr)
{
const auto iSize = pAryStrText->GetSize();
for (i = 0; i < iSize; i++)
{
if (pAryStrText->GetAt(i) == strText)
{
// Found him!
bFound = true;
break;
}
}
}
if (!bFound)
i = -1;
return (int)i;
}
If I convert my CStringArray into a std::vector<CString is it going to be simpler to achieve the same PerformShuffle and LocateText methods?
Should I just stay with CStringArray?
I know of 1 (ONE!) benefit of MFC array over std::vector - they support MFC-style serialization. If you use it - you may be stuck.
However, if you don't - I would use std::vector<CString>. Your LocateText (that is overly verbose) will become obsolete - just use find
Also, your ShuffleArray is very inefficient (remove/insert one item at a time). Using a vector will allow you to do something like Best way to extract a subvector from a vector?

Populating std::map and calling find return end() even though the entry is in the map

So, I create this map on the fly so I can update the image type for all the images in the library (currently over 1 million). The map populates well, HOWEVER, despite visually seeing the extension ("jpg") added to the map, std::map::find("jpg") == std::map::end() always. What gives.
void CImageLibrary::ThreadedUpdateImageLibraryFileDuringOpen(shared_ptr<ScanData> pScanData)
{
size_t nCount = pScanData->GetImageLibrary()->m_imageEntries.count();
std::map<std::wstring, CImageType *> mapImageExtToType;
for (const auto &pImageType : pScanData->GetImageLibrary()->m_vecImageTypes)
{
std::vector<std::wstring> vecExtensions;
pImageType->GetAssociatedExtensions(vecExtensions);
for (auto ext : vecExtensions)
{
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
mapImageExtToType[ext] = pImageType;
}
}
for (size_t idx = 0; idx < nCount; ++idx)
{
CImageEntry *pEntry = pScanData->GetImageLibrary()->m_imageEntries[idx];
if (pEntry && !pEntry->GetImageType())
{
wstring &wsPath = pEntry->GetPathName(), wsExt;
size_t nIdx = wsPath.find_last_of('.');
wsExt = wsPath.substr(nIdx + 1);
std::transform(wsExt.begin(), wsExt.end(), wsExt.begin(), ::tolower);
auto Itor = mapImageExtToType.find(wsExt);
if (Itor == mapImageExtToType.end())
pEntry->m_pImageType = nullptr;
else
pEntry->m_pImageType = Itor->second;
}
}
pScanData->GetBkgndThreadManager()->MarkOperationThreadDone(std::this_thread::get_id());
}

How can i download only new records from Anviz EP300?

All,
Currently i'm using Anviz EP300 time attendance machine. I need to download only new records from device.
I'm using following sdk. Click here
There is some method already have in sdk. Which is i used like...
int i = 0;
int Ret = 0;
int RecordCount = 0;
int RetCount = 0;
int pClockings = 0;
int pLongRun = 0;
CKT_DLL.CLOCKINGRECORD clocking = new CKT_DLL.CLOCKINGRECORD();
clocking.Time = new byte[20];
int ptemp = 0;
ProgressBar1.Value = 0;
//If CKT_GetClockingNewRecordEx(IDNumber, pLongRun) Then 'IF GET NewRecord
if (CKT_DLL.CKT_GetClockingNewRecordEx(IDNumber, ref pLongRun) != 0) //IF GET Record
{
while (true)
{
Ret = CKT_DLL.CKT_GetClockingRecordProgress(pLongRun, ref RecordCount, ref RetCount, ref pClockings);
if (RecordCount > 0)
{
ProgressBar1.Maximum = RecordCount;
}
if (Ret == 0)
{
return;
}
if (Ret != 0)
{
ptemp = pClockings;
for (i = 1; i <= RetCount; i++)
{
PCopyMemory(ref clocking, pClockings, CKT_DLL.CLOCKINGRECORDSIZE);
pClockings = pClockings + CKT_DLL.CLOCKINGRECORDSIZE;
insertTimeAttendance(clocking.PersonID, clocking.Stat, Encoding.Default.GetString(clocking.Time), clocking.ID);
ProgressBar1.Value += 1;
}
if (ptemp != 0)
{
CKT_DLL.CKT_FreeMemory(ptemp);
}
}
if (Ret == 1)
{
return;
}
}
}
CKT_GetClockingNewRecordEx that method should be return new records. But it returns all records.
I guess, there is should be one method or config should be mark as downloaded.
Anyone some idea or solution?
Thanks,
Eba
I created the SDK you downloaded (basically written in Vb, and I just convert it to C#)
Actually for the Anviz EP300 device, there is no way you can just retrieve new records,neither retrieve user lists (for example), at list with that SDK.. It has a lot of methods, but unfortunately few of them works fine. You will have to Use CKT_GetClockingRecordEx, instead of CKT_DLL.CKT_GetClockingNewRecordEx

How can I correct the error "Cross-thread operation not valid"?

This following code gives me the error below . I think I need "InvokeRequired" . But I don't understand how can I use?
error:Cross-thread operation not valid: Control 'statusBar1' accessed from a thread other than the thread it was created on.
the code :
public void CalculateGeneration(int nPopulation, int nGeneration)
{
int _previousFitness = 0;
Population TestPopulation = new Population();
for (int i = 0; i < nGeneration; i++)
{
if (_threadFlag)
break;
TestPopulation.NextGeneration();
Genome g = TestPopulation.GetHighestScoreGenome();
if (i % 100 == 0)
{
Console.WriteLine("Generation #{0}", i);
if ( ToPercent(g.CurrentFitness) != _previousFitness)
{
Console.WriteLine(g.ToString());
_gene = g;
statusBar1.Text = String.Format("Current Fitness = {0}", g.CurrentFitness.ToString("0.00"));
this.Text = String.Format("Sudoko Grid - Generation {0}", i);
Invalidate();
_previousFitness = ToPercent(g.CurrentFitness);
}
if (g.CurrentFitness > .9999)
{
Console.WriteLine("Final Solution at Generation {0}", i);
statusBar1.Text = "Finished";
Console.WriteLine(g.ToString());
break;
}
}
}
}
Easiest for reusability is to add a helper function like:
void setstatus(string txt)
{
Action set = () => statusBar1.Text = txt;
statusBar1.Invoke(set);
}
Or with the invokerequired check first:
delegate void settextdelegate(string txt);
void setstatus(string txt)
{
if (statusBar1.InvokeRequired)
statusBar1.Invoke(new settextdelegate(setstatus), txt);
else
statusBar1.Text = txt;
}
Either way the status can then be set like
setstatus("Finished");
For completeness I should add that even better would be to keep your calculating logic separated from your form and raise a status from within your calculating functionality that can be hanled by the form, but that could be completely out of scope here.

Is there a way to use normal ASCII characters (like a comma) as wxWidgets menu accelerators?

I want a few menu entries that show accelerators that are normal keys, like the space-bar or comma key, but I don't want wxWidgets to make those accelerators itself (because then they can't be used anywhere in the program, including in things like edit boxes).
Unfortunately, wxWidgets insists on always making anything it recognizes in that column into an accelerator under its control, and simply erases anything it doesn't recognize.
I'm looking for some way to either put arbitrary text into the accelerator column (which I don't think exists, I've looked at the source code), or get 'hold of the accelerator table used for the menus so I can modify it myself (haven't found it yet). Can anyone point me in the right direction?
You can try wxKeyBinder. It allows you to bind hotkeys to commands (usually menu entries), save/load/add/remove/modify ... them easily
I couldn't find a way to access the menu's accelerator keys directly, but modifying the accelerator menu text works just as well. Here's the code I came up with:
In a header file:
class accel_t {
public:
// If idcount == -1, idlist must be null or terminated with a -1 entry.
accel_t(): mMenu(0) { }
accel_t(wxMenuBar *m, int *idlist = 0, int idcount = -1);
void reset(wxMenuBar *m, int *idlist = 0, int idcount = -1);
void restore() const;
void remove() const;
private: //
struct accelitem_t {
accelitem_t(int _id, wxAcceleratorEntry _key): id(_id), hotkey(_key) { }
int id;
wxAcceleratorEntry hotkey;
};
typedef std::vector<accelitem_t> data_t;
void noteProblemMenuItems(wxMenu *m);
static bool isProblemAccelerator(wxAcceleratorEntry *a);
wxMenuBar *mMenu;
data_t mData;
};
In a cpp file:
accel_t::accel_t(wxMenuBar *m, int *idlist, int idcount) {
reset(m, idlist, idcount);
}
void accel_t::reset(wxMenuBar *m, int *idlist, int idcount) {
mMenu = m;
mData.clear();
if (idlist == 0) {
for (int i = 0, ie = m->GetMenuCount(); i != ie; ++i)
noteProblemMenuItems(m->GetMenu(i));
} else {
if (idcount < 0) {
int *i = idlist;
while (*i != -1) ++i;
idcount = (i - idlist);
}
for (int *i = idlist, *ie = i + idcount; i != ie; ++i) {
wxMenuItem *item = mMenu->FindItem(*i);
if (item) {
wxAcceleratorEntry *a = item->GetAccel();
if (a != 0) mData.push_back(accelitem_t(*i, *a));
}
}
}
}
bool accel_t::isProblemAccelerator(wxAcceleratorEntry *a) {
if (a == 0) return false;
int flags = a->GetFlags(), keycode = a->GetKeyCode();
// Normal ASCII characters, when used with no modifier or Shift-only, would
// interfere with editing.
if ((flags == wxACCEL_NORMAL || flags == wxACCEL_SHIFT) &&
(keycode >= 32 && keycode < 127)) return true;
// Certain other values, when used as normal accelerators, could cause
// problems too.
if (flags == wxACCEL_NORMAL) {
if (keycode == WXK_RETURN ||
keycode == WXK_DELETE ||
keycode == WXK_BACK) return true;
}
return false;
}
void accel_t::noteProblemMenuItems(wxMenu *m) {
// Problem menu items have hotkeys that are ASCII characters with normal or
// shift-only modifiers.
for (size_t i = 0, ie = m->GetMenuItemCount(); i != ie; ++i) {
wxMenuItem *item = m->FindItemByPosition(i);
if (item->IsSubMenu())
noteProblemMenuItems(item->GetSubMenu());
else {
wxAcceleratorEntry *a = item->GetAccel();
if (isProblemAccelerator(a))
mData.push_back(accelitem_t(item->GetId(), *a));
}
}
}
void accel_t::restore() const {
if (mMenu == 0) return;
for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
++i)
{
wxMenuItem *item = mMenu->FindItem(i->id);
if (item) {
wxString text = item->GetItemLabel().BeforeFirst(wxT('\t'));
wxString hotkey = i->hotkey.ToString();
if (hotkey.empty()) {
// The wxWidgets authors apparently don't expect ASCII
// characters to be used for accelerators, because
// wxAcceleratorEntry::ToString just returns an empty string for
// them. This code deals with that.
int flags = i->hotkey.GetFlags(), key = i->hotkey.GetKeyCode();
if (flags == wxACCEL_SHIFT) hotkey = wx("Shift-") + wxChar(key);
else hotkey = wxChar(key);
}
item->SetItemLabel(text + '\t' + hotkey);
}
}
}
void accel_t::remove() const {
if (mMenu == 0) return;
for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
++i)
{
wxMenuItem *item = mMenu->FindItem(i->id);
if (item) {
wxString text = item->GetItemLabel().BeforeFirst(wxT('\t'));
item->SetItemLabel(text);
}
}
}
The easiest way to use it is to create your menu-bar as normal, with all accelerator keys (including the problematic ones) in place, then create an accel_t item from it something like this:
// mProblemAccelerators is an accel_t item in the private part of my frame class.
// This code is in the frame class's constructor.
wxMenuBar *menubar = _createMenuBar();
SetMenuBar(menubar);
mProblemAccelerators.reset(menubar);
It will identify and record the accelerator keys that pose problems. Finally, call the remove and restore functions as needed, to remove or restore the problematic accelerator keys. I'm calling them via messages passed to the frame whenever I open a window that needs to do standard editing.

Resources