Performance Notes
In order for a class wrapper to produce equivalent assembly as the wrapped type (e.g. a raw pointer, integer, ...), it must satisfy certain traits that in general are dependent on the platform ABI.
Any class (no matter how complicated) will be optimized out in all cases, except when passed as an argument to or returned from a function. The argument passing conventions depend on the platform ABI, but in general, simple types like raw pointers or integers as well as small classes of certain type will be passed in registers (i.e. by value), while big classes on a stack (by reference).
Example1: g++ 4.8.2, 4.9.2, 5.1.0 on 64bit will pass classes by value if and
only if the class is
trivially
copyable and its
size (measured using sizeof
) is less or equal to 16 bytes.
Example2: icpc 15.0.1 on 64bit will pass by value if and only if the class is trivially copyable and size less or equal to 16 bytes.
Other platforms might differ. Some authors propose to pass classes by value if they are trivially copyable as well as a standard layout type. So it is probably a good idea to make it both trivially copyable as well as a standard layout type. However, it does not seem to need to have a trivial default constructor (i.e. neither trivial nor a POD seems to be required).
Type Traits
If the class is trivially copyable (std::is_trivially_copyable) as well as has a trivial default constructor (std::is_trivially_default_constructible) then it is trivial (std::is_trivial). If in addition it is a standard layout type (std::is_standard_layout), then it is a POD (std::is_pod).
Example 1
Old:
int *a;
{
int b=1;
a = &b;
}
*a = 5; // Dangling, undefined behavior
New:
Ptr<int> a;
{
#ifdef DEBUG_MODE
UniquePtr<int> b(new int(1));
#else
int b=1;
#endif
#ifdef DEBUG_MODE
a = b.ptr();
#else
a = ptrFromRef(b);
#endif
}
*a = 5; // Dangling, throws an exception in Debug mode
Example 2
Old:
class A {
private:
std::map<int, int> m;
public:
std::map<int, int> *get_access() {
return &m;
}
};
New:
class A {
private:
#ifdef DEBUG_MODE
UniquePtr<std::map<int, int>> m;
#else
std::map<int, int> m;
#endif
public:
Ptr<std::map<int, int>> get_access() {
#ifdef DEBUG_MODE
return m.ptr();
#else
return ptrFromRef(m);
#endif
}
};
Example 3
Code (original):
UserIdentity *IdentityManager::lookupHostname(const QString &hostname) const
{
QString ohost = ContactIDValidator::hostnameFromID(hostname);
if (ohost.isNull())
ohost = hostname;
if (!ohost.endsWith(QLatin1String(".onion")))
ohost.append(QLatin1String(".onion"));
for (QList<UserIdentity*>::ConstIterator it = m_identities.begin(); it != m_identities.end(); ++it)
{
if (ohost.compare((*it)->hostname(), Qt::CaseInsensitive) == 0)
return *it;
}
return 0;
}
Code (original):
ContactUser *ContactIDValidator::matchingContact(const QString &text) const
{
ContactUser *u = 0;
if (m_uniqueIdentity)
u = m_uniqueIdentity->contacts.lookupHostname(text);
return u;
}
And (original):
void UserIdentity::handleIncomingAuthedConnection(Connection *conn)
{
if (conn->purpose() != Connection::Purpose::Unknown)
return;
QString clientName = conn->authenticatedIdentity(Connection::HiddenServiceAuth);
if (clientName.isEmpty()) {
BUG() << "Called to handle incoming authed connection without any authed name";
return;
}
ContactUser *user = contacts.lookupHostname(clientName);
if (!user) {
// This client can start a contact request, for example. The purpose stays unknown, and the
// connection will be killed if the purpose isn't changed before the timeout.
qDebug() << "Have an incoming connection authenticated as unknown client" << clientName;
return;
}
qDebug() << "Incoming connection authenticated as contact" << user->uniqueID << "with hostname" << clientName;
user->assignConnection(conn);
if (conn->parent() != user) {
BUG() << "Connection wasn't claimed after authentication";
conn->close();
}
}