This adventure ate my breakfast, lunch, and dinner for a few days… The biggest problem is that all of my reference material consistently gives examples such as the one that follows:
 |
This would work great if I were trying to return an int or double because most, if not all, of the articles/books/blogs will demonstrate how easily this can be done using variations of the example on the left. My problem started when I needed to overload the subscript operator to return a shared_ptr<DataColumn>. This is where I left a bloody mess on my desktop as I banged my head against it trying to find a “modern” way – it wasn’t readily available and it cost me a few days…. |
So if you’re like me and don’t want to create get/set methods (and you ended up with the error caused by the subscript referenced by the arrow below) keep reading, I do have an easy answer (although it is quite ugly).

The answer is to dereference first (highlighted content on lines 138-139) and then use the subscript operator to obtain your data references. Likewise you can use the –>operator[] as shown on lines 151-156.
Note below (arrows) that my classes have static RefCount ints so that I can ensure I don’t leak memory as I build my ADO Style wrapper for Sqlite (http://msdn.microsoft.com/en-us/library/vstudio/hfx3s9wd(v=vs.100).aspx). The notion being that as I code I can place asserts at points where I expect there to be no RefCounts for a given class (trap memory leaks when I break something).

DataColumnFixture.cpp in its entirety (classes in test fixture during POC TDD):
#include "stdafx.h"
#include "CppUnitTest.h"
#include "ILogger.h"
#include "DebugLogger.h"
#include <vector>
#include <map>
#include <string>
#include <Windows.h>
#include <memory>
#include <sstream>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
extern void OutputDebugDateTime();
namespace lib
{
using std::string;
using std::vector;
using std::map;
using std::pair;
using std::shared_ptr;
using std::make_shared;
using std::stringstream;
}
// IOC Container stub -----------------------------
template <typename T>
static T Resolve() { return new DebugLogger(); }
// typeof() stub-----------------------------------
using type = string;
type typeof(string typeName) { return typeName; }
const string String = "String";
const string Int = "int";
namespace GwnDataLibTests
{
class DataColumn
{
bool isInitialized = false;
ILogger *logger{ Resolve<ILogger*>() };
public:
static int RefCount;
string Caption;
string ColumnName;
type Type;
int Ordinal;
DataColumn();
~DataColumn();
DataColumn(string caption, string columnName, type type, int ordinal);
};
int DataColumn::RefCount = 0;
DataColumn::DataColumn()
{
logger->Log("%d: +DataColumn()\r\n", ++RefCount);
}
DataColumn::DataColumn(string caption, string columnName, type type, int ordinal)
{
ColumnName = columnName;
Caption = caption;
Ordinal = ordinal;
Type = type;
logger->Log("%d: +DataColumn(\"%s\", \"%s\", %d)\r\n",
++RefCount, columnName.c_str(), type.c_str(), ordinal);
}
DataColumn::~DataColumn()
{
logger->Log("%d: ~DataColumn()\r\n", --RefCount);
delete logger;
}
class DataColumnCollection
{
int ordinal{ 0 };
DataColumn currentColumn;
lib::vector<lib::shared_ptr<DataColumn>> List;
lib::map<string, lib::shared_ptr<DataColumn>> columnFromName;
ILogger *logger{ Resolve<ILogger*>() };
public:
static int RefCount;
DataColumnCollection();
~DataColumnCollection();
lib::shared_ptr<DataColumn> operator[](int i);
const lib::shared_ptr<DataColumn> operator[](int i) const;
void Add(string columnName, type columnType);
};
int DataColumnCollection::RefCount = 0;
lib::shared_ptr<DataColumn> DataColumnCollection::operator[](int i)
{
return List[i];
}
const lib::shared_ptr<DataColumn> DataColumnCollection::operator[](int i) const
{
return List[i];
}
DataColumnCollection::DataColumnCollection()
{
logger->Log("%d: +DataColumnCollection()\r\n", ++RefCount);
}
DataColumnCollection::~DataColumnCollection()
{
logger->Log("%d: ~DataColumnCollection()\r\n",--RefCount);
delete logger;
}
void DataColumnCollection::Add(string columnName, type columnType)
{
logger->Log("\t Adding column %s typeof(%s)\r\n",
columnName.data(), columnType.data());
auto col = lib::shared_ptr<DataColumn>(
new DataColumn { "Caption", columnName, columnType, ordinal++ });
List.push_back(col);
columnFromName[columnName] = col;
}
TEST_CLASS(DataColumnFixture)
{
public:
TEST_METHOD(CanGetListIndex)
{
ILogger *logger{ Resolve<ILogger*>() };
if (true){ // Scope for shared ptrs so that destructors will kick in
logger->Log("\t Instantiating DataColumnCollection\r\n");
auto myTest = lib::make_shared<DataColumnCollection>();
myTest->Add("Name", typeof(String));
myTest->Add("Age", typeof(Int));
auto mt1 = (*myTest)[0]; // myTest->operator[](0);
auto mt2 = (*myTest)[1]; // myTest->operator[](1);
// Assert that we get expected results
Assert::AreEqual(string{ "Name" }, mt1->ColumnName);
Assert::AreEqual(typeof(String), mt1->Type);
Assert::AreEqual(0, mt1->Ordinal);
Assert::AreEqual(string{ "Age" }, mt2->ColumnName);
Assert::AreEqual(typeof(Int), mt2->Type);
Assert::AreEqual(1, mt2->Ordinal);
// An alternate way - doesn't look much better...
auto mt1a = myTest->operator[](0);
Assert::AreEqual(mt1->ColumnName, mt1a->ColumnName);
Assert::AreEqual(mt1->Type, mt1a->Type);
Assert::AreEqual(mt1->Ordinal, mt1a->Ordinal);
auto mt2a = myTest->operator[](1);
Assert::AreEqual(mt2->ColumnName, mt2a->ColumnName);
Assert::AreEqual(mt2->Type, mt2a->Type);
Assert::AreEqual(mt2->Ordinal, mt2a->Ordinal);
}
logger->Log("**************************\r\n");
logger->Log("DataColumn: %d\r\n", DataColumn::RefCount);
logger->Log("DataColumnCollection: %d\r\n ", DataColumnCollection::RefCount);
logger->Log("**************************\r\n");
delete logger;
}
};
}
ILogger.h
#pragma once
#include <string>
#include <Windows.h>
using std::string;
using std::wstring;
class ILogger
{
public:
wstring virtual Log(const string message, ...) = 0;
wstring virtual Log(const wstring message, ...) = 0;
virtual ~ILogger() = 0
{
OutputDebugString(L"~ILogger()\r\n");
}
};
DebugLogger.h
#pragma once
#include "ILogger.h"
#include <vector>
using std::wstring;
class DebugLogger : public ILogger
{
public:
static int RefCount;
DebugLogger();
~DebugLogger();
wstring Log(const string message, ...) override;
wstring Log(const wstring message, ...) override;
};
#include "pch.h"
#include "DebugLogger.h"
#include <Windows.h>
#include <memory>
#include <string>
using std::wstring;
using std::vector;
using std::begin;
using std::end;
void OutputDebugDateTime()
{
string date{ __DATE__ };
string time{ __TIME__ };
string dateTime = date + " " + time;
wstring wDateTime(begin(dateTime), end(dateTime));
OutputDebugString(wDateTime.data());
OutputDebugString(L" ");
}
int DebugLogger::RefCount = 0;
DebugLogger::DebugLogger()
{
Log("%d: +DebugLogger()\r\n", ++RefCount);
}
DebugLogger::~DebugLogger()
{
Log("%d: ~DebugLogger() ", --RefCount);
}
wstring DebugLogger::Log(const string str, ...)
{
// Convert string to char*
auto formattedMessage = str.c_str();
// Use vector for buffer so we can resize if applicable
vector<char> buffer;
// Default size for buffer is twice size of str (parameter)
int sizeOfBuffer = str.size() * 2;
buffer.resize(sizeOfBuffer);
// Initialize our variable argument list. Note that
// for va_start we send the last parameter prior to ...
va_list argPtr;
va_start(argPtr, str);
// _vsnwprintf_s returns negative value if an output error
// occurs assumption is that buffer isn't big enough, we'll
// try abandon if failure continues.
auto counter = 0;
while (++counter < 4)
{
// format the string using variable arguments -> buffer
int ret = _vsnprintf_s(
buffer.data(), // char *buffer
sizeOfBuffer, // size_t sizeOfBuffer
_TRUNCATE, // size_t count
formattedMessage, // const char *format
argPtr // va_list argptr
);
if (ret != -1)
break;
else {
// Increase size of buffer - parameter size may be larger
// than the default allocation of 2 x str length
sizeOfBuffer *= 2;
buffer.resize(sizeOfBuffer);
}
}
va_end(argPtr);
// Convert buffer to wstring so we can easily get wchar_t*
// for the OutputDebugString(LPCWSTR*) parameter
wstring message(buffer.begin(), buffer.end());
OutputDebugDateTime();
OutputDebugString(message.data());
//OutputDebugString(L"\r\n");
return message;
}
wstring DebugLogger::Log(const wstring str, ...)
{
// Convert wstring to wchar_t*
const wchar_t* formattedMessage = str.data();
// Use vector for buffer so we can resize if applicable
vector<wchar_t> buffer;
// Default size for buffer is twice size of str (parameter)
auto sizeOfBuffer = wcslen(formattedMessage) * 2;
buffer.resize(sizeOfBuffer);
// Initialize our variable argument list. Note that
// for va_start we send the last parameter prior to ...
va_list argPtr;
va_start(argPtr, str);
// _vsnwprintf_s returns negative value if an output error
// occurs assumption is that buffer isn't big enough, we'll
// try abandon if failure continues.
auto counter = 0;
while (++counter < 4)
{
// format the string using variable arguments -> buffer
int ret = _vsnwprintf_s(
buffer.data(), // wchar_t *buffer
sizeOfBuffer, // size_t sizeOfBuffer
_TRUNCATE, // size_t count
formattedMessage, // const wchar_t *format
argPtr // va_list argptr
);
if (ret != -1)
break;
else
{
// Increase size of buffer - parameter size may be larger
// than the default allocation of 2 x str length
sizeOfBuffer *= 2;
buffer.resize(sizeOfBuffer);
}
}
va_end(argPtr);
// Convert buffer to wstring so we can easily get wchar_t*
// for the OutputDebugString(LPCWSTR*) parameter
wstring message(buffer.begin(), buffer.end());
OutputDebugDateTime();
OutputDebugString(message.data());
//OutputDebugString(L"\r\n");
return message;
}