123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- 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) { 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));
- == Exception-based wrappers ==
- A group of experimental classes which always return objects while
- error codes are signalled via exceptions (rather than return codes).
- This makes it possible to write code which is so terse that I am not
- convinced yet using these classes everywhere is a good idea (it makes
- it harder to identify which function exactly failed).
- Traditional:
- //IMsgStore *store;
- IMAPIFolder *rcv;
- IMessage *msg;
- IUnknown *entry;
- int ret = store->GetReceiveFolder(&rcv);
- if (ret != hrSuccess)
- goto exit;
- ret = store->OpenEntry(rcv, nullptr, MAPI_MODIFY, &entry);
- if (ret != hrSuccess)
- goto exit;
- ret = entry->CreateMessage(nullptr, 0, &msg);
- if (ret != hrSuccess)
- goto exit;
- msg->foo(); //...
- exit:
- if (ret != hrSuccess)
- fprintf(stderr, "%s\n", GetMAPIErrorDescription(ret));
- if (rcv != nullptr)
- rcv->Release();
- New:
- //KStore store;
- try {
- auto new_msg = store
- .open_entry(store.get_receive_folder(), nullptr, MAPI_MODIFY)
- .create_message(nullptr, 0)
- new_msg->foo(); //...
- } catch (KMAPIError &e) {
- fprintf(stderr, "%s\n", e.what());
- }
- This group of classes is the only one that require Makefile.am to be augmented
- with libkchl.la / -lkchl.
|