libkchl - C++ API for shorter code doing MAPI


== What is encompassed by KCHL ==

* <kopano/ECRestriction.h>: ECRestriction, ECAndRestriction,
  ECPropertyRestriction, etc.: classes for construct restrictions, and to later
  export to a bare MAPI SRestriction.

* <kopano/memory.hpp>: memory_ptr<T>: smart pointer class like unique_ptr describing ownership of an
  object whose lifetime is bound by MAPIAllocateBuffer/MAPIFreeBuffer.

* <kopano/memory.hpp>: object_ptr<T>: smart pointer class like unique_ptr(!)
  holding an object (like shared_ptr(!)) whose lifetime is bound by
  IUnknown::AddRef/Release.

* <kopano/tie.hpp>: unique_tie: glue function to bind a std::unique_ptr<T> to a
  function taking T*.

* <kopano/automapi.hpp>: AutoMAPI: Wrapepr class to invoke
  MAPIUninitialize at destruction time.

* <kopano/hl.hpp>: K* classes: Exception-based wrapper classes for MAPI
  object pointers.


== ECRestriction ==

Classic MAPI approach:

	SRestriction r, tmp[2];
	r.rt = RES_OR;
	r.res.resOr.cRes = 2;
	r.res.resOr.lpRes = tmp;
	tmp[0].rt = RES_EXIST;
	tmp[0].res.resExist.ulPropTag = PR_EC_IMAP_ID;
	tmp[1].rt = RES_PROPERTY;
	tmp[1].res.resProperty.relop = RELOP_EQ;
	tmp[1].res.resProperty.ulPropTag = PR_EC_IMAP_ID;
	tmp[1].res.resProperty.lpProp = &pv;

With ECRestriction classes:

	ECOrRestriction r(
		ECExistRestriction(PR_EC_IMAP_ID) +
		ECRestrictionProperty(RELOP_EQ, PR_EC_IMAP_ID, &pv,
			ECRestriction::Shallow));

KC < 8.3 allowed for the last argument to be optional; this has been abolished
so that there is always a mindful mode selection by the developer (and review
thereof).


== memory_ptr ==

KCHL::memory_ptr works much like a std::unique_ptr made to use MAPIFreeBuffer,
but has a number of extensions for source-code compatibility such as implicit
conversions. The use of unique_ptr/memory_ptr allows for shorter
code.

Traditional:

	foo *obj;
	MAPIAllocateBuffer(sizeof(*obj), &obj);
	memset(obj, 0, sizeof(obj));
	...
	exit:
	MAPIFreeBuffer(obj);

With unique_ptr:

	//struct mapideleter { void operator()(void *x) const { MAPIFreeBuffer(x); }}
	std::unique_ptr<foo, mapideleter> obj;
	MAPIAllocateBuffer(sizeof(*obj), &unique_tie(obj));
	memset(obj.get(), 0, sizeof(foo));

With memory_ptr:

	memory_ptr<foo> obj;
	MAPIAllocateBuffer(sizeof(*obj), &~obj);
	memset(obj, 0, sizeof(foo));

Since one cannot know in advance whether such a function will write to obj or
leave it untouched, the & operator had been defined to always free obj first to
ensure there will be no leaks. As memory_ptr was used in more and more places,
it turned out that in some instances, such freeing is undesired (e.g. near
GetNameFromIDs). As a consequence, freeing has been made explicit using the
~ operator, not-freeing is expressed with the + operator, and not using either
of ~ or + leads to a compile error, to catch unmindful uses of &.

== object_ptr ==

The class implicitly invokes the object's Release function when the
object_ptr goes out of scope. This allows for shorter code.

Classic MAPI approach:

	{
		IMessage *msg;
		int ret = store->CreateMessage(&msg);
		if (ret != hrSuccess)
			goto exit;
		ret = msg->foo();
		if (ret != hrSuccess)
			goto exit;
		exit:
		if (msg != nullptr)
			msg->Release();
		return ret;
	}

Modern approach:

	{
		object_ptr<IMessage> msg;
		int ret = store->CreateMessage(&~msg);
		if (ret != hrSuccess)
			return hr;
		ret = msg->foo();
		if (ret != hrSuccess)
			return ret;
	}


== unique_tie ==

Instead of writing

	std::unique_ptr<Foo> g;
	Foo *f;
	somefunction(&f);
	g.reset(f);

the unique_tie function makes it possible to do without the explicit temporary
'f':

	std::unique_ptr<Foo> g;
	somefunction(&unique_tie(g));


== adrlist_ptr, rowset_ptr, rowlist_ptr ==

There are three extra typedefs for the purpose of portraying the "individual
allocation" strategy often used for ADRLIST, SRowSet and ROWLIST:

	typedef memory_ptr<ADRLIST, rowlist_delete> adrlist_ptr;
	typedef memory_ptr<SRowSet, rowlist_delete> rowset_ptr;
	typedef memory_ptr<ROWLIST, rowlist_delete> rowlist_ptr;

(https://msdn.microsoft.com/en-us/library/office/cc842180.aspx) When every
SPropValue within such a type is allocated independently — with
MAPIAllocateBuffer rather than MAPIAllocateMore —, the rowlist deleter must be
used. Conversely, if a certain block of code depends on that the SPropValue not
be freed, such as when the SPropValue is a local automatic, then rowlist_ptr
must *not* be used.

	memory_ptr<ROWLIST> rowlist;
	SPropValue foo;
	MAPIAllocateMemory(CbNewROWLIST(1), &~rowlist);
	foo.ulPropTag = PR_FOO;
	rowlist->aEntries[0].rgPropVals = &foo;
	rowlist->cEntries = 1;
