Problem with C++ wrapper and gpgme

Robert J. Hansen cortana at earthlink.net
Mon May 26 08:51:02 CEST 2003


> Why did that surprise you?

Oh, boy, this one's guaranteed to set off a flamefest.  Pardon me while
I put on my asbestos longjohns.  If I manage to piss people off, please
accept my apologies in advance.

It surprised me both due to speed and ease-of-use issues.  The speed
issue was surprising because I was expecting that GPGME, being written
as the canonical way of interacting with GnuPG, would be optimized for
speed and efficiency.  For small keyrings GPGME beat the handrolled
solution by small margins, but as keyrings grew larger GPGME fell
significantly behind.

Ease-of-use was likewise surprising.  It turned out to be considerably
easier to interact with GnuPG through a homegrown C++ wrapper that
ripped the data from a gpg --list-keys --with-colons --fingerprint than
it was through GPGME.

As a for-example, consider the code we write if we want to display every
valid, nonrevoked UID.  (This isn't a completely fanciful example,
either: it's based on a real project I'm working on.)

// C++ version

int main(void)
{
  try {
    Keyring kr;
    vector<Key>::const_iterator keyiter = kr.keys().begin();
    while ( keyiter != kr.keys.end() ) {
      vector<UID>::const_iterator uiditer = keyiter->uids().begin();
      while ( uiditer != keyiter->uids().end() ) {
         if ( !uiditer->revoked() && !uiditer->invalid() )
           cout << uiditer->name << " <" << uiditer->email
                << ">" << endl;
         uiditer++;
      }
      keyiter++;
    } 
  } catch (GnuPGException &err) {
  // handle error condition.  Handwave it here.
  }
}




/* C version */
 
int error_handler(GpgmeError err)
{
  /* handle errors appropriately, return 0 if it's recoverable, */
  /* 1 if it's not.  We're going to handwave it here.           */
  return 0;
}
 
int main(void)
{
  GpgmeCtx ctx;
  GpgmeKey key;
  if ( error_handler(gpgme_new(&ctx)) ) 
    return;
  gpgme_set_keylist_mode( ctx, GPGME_KEYLIST_MODE_LOCAL );
  gpgme_op_keylist_start( ctx, NULL, 0 );
  while ( gpgme_op_keylist_next(ctx, &key) == GPGME_No_Error ) {
    int idx = 0;
    const char *name = NULL, *email = NULL;
    unsigned long revoked = 0, invalid = 0;
    do {
      name = 
        gpgme_key_get_string_attr( key, GPGME_ATTR_NAME, NULL, idx );
      email = 
        gpgme_key_get_string_attr( key, GPGME_ATTR_EMAIL, NULL, idx );
      revoked =
        gpgme_key_get_ulong_attr( key, GPGME_ATTR_UID_REVOKED, NULL, idx
);
      invalid =
        gpgme_key_get_ulong_attr( key, GPGME_ATTR_UID_INVALID, NULL, idx
);
      if ( name && !revoked && !invalid )
        printf( "%s <%s>\n", name, email );
      idx++;
    } while ( name != NULL );
  }
  gpgme_op_keylist_end( ctx );
  gpgme_release( ctx );
  return 0;
}




The C++ code is, from where I stand, easier to read, easier to write,
easier to correctly use (no manual keylist_end(), no gpgme_release(),
etc.), and has a significant speed improvement for large keyrings.  

Just to be clear, I understand people on-list may well disagree with me
on the easier-tos.  "Easier" is usually a matter of personal opinion.

-- 
Robert J. Hansen <cortana at earthlink.net>





More information about the Gnupg-devel mailing list