sobota, 12 listopada 2016

base class operator== detection

Scenario:
Class for items storage. Items can be added, read, updated. In case of modification callback notification is sent to all listeners.
To discover modification operator==() can be used. It seems to be natural, non-intrusive solution.
Seems to be fine, but problem comes with inheritance.
Consider types:

struct A {
A(int v) : v(v) {}

int v;

friend bool operator==(const A &lhs, const A &rhs)
{
return lhs.v == rhs.v;
}
};
struct B : A {
B(int v, int w) : A(v), w(w) {}

int w;
};


Note that opeartor==() is defined as friend (therefore not class member - see: ADL, friend name injection, Barton–Nackman trick) but it can also be defined as member function or global function.


The potential problem is visible here:

A a1{1}, a2{1};

std::cout << (a1 == a2); B b1{1,1}, b2{1,2}; std::cout << (b1 == b2);


Both print '1', but is 'b1' really equal to 'b2'?
According to current implementation yes, because only A part of B-type object is compared.
The real problem is no notification of such potential problem. And when generic code is used to compare object, where types are defined in different files it might be easy to forget about operator==() definition for class B.
To avoid potential problem it might be better to decide that such situation is prohibited and shall end in compile-time error.
Required is compile time checking if some type (some, because comparison will be used in generic code) has defined operator==.
Tool to solve the problem might be found e.g. in Boost (has_equal_to from typetraits or Concept Check Library), but simple solution is presented here http://stackoverflow.com/a/6536204/122054.
For C++98:

namespace CHECK
{
class No { bool b[2]; };
template No operator== (const T&, const Arg&);

bool Check (...);
No& Check (const No&);

template
struct EqualExists
{
enum { value = (sizeof(Check(*(T*)(0) == *(Arg*)(0))) != sizeof(No)) };
};
}


Simplified version for C++11:

namespace CHECK
{
struct No {};
template No operator== (const T&, const Arg&);

template
struct EqualExists
{
enum { value = !std::is_same<decltype(*(T*)(0) == *(Arg*)(0)), No>::value };
};
}


Using CHECK::EqualExists::value with static assert allows to detect potential problem.

niedziela, 12 czerwca 2016

Old dog,old tricks (in C++)

Compile time safety, compile time error handling - all about typesystem, constness, ...

C-array safety

If function uses constant length array it is tempting to use:


void f(int t[3])
{
...
t[2] = ...;
}

...

int t[3];
f(t);


as array size is merely for human-programmer, it is possible to use:


int t[4];
f(t);


which perhaps is ok (but not nice).
But it is also possible to do


int t[2];
f(t);


Which for sure is wrong.

Solution to ensure strict array size is:


void f(int (&t)[3])
{
...
}


Now only three elements arrays are allowed.
BTW - above construct is frequently used for C-array handling in templates.

poniedziałek, 7 grudnia 2015

C++11 and initialization

Uniform initialization in C++11 can be tricky.

Invocations mean something completely different and also give different results:

std::vector<int> v(1); // "normal" constructor invocation with (int) param - creates vector<int> with 1 element initialized to default value (i.e. 0)
std::vector<int> v{1}; // invocation of constructor with initializer_list<> param - content of initializer_list is copied into vector (i.e. one value of 1)


Following invocations also mean something completely different and give different results:

std::vector<int> v(1, 1); // "normal" constructor invocation with (int,int) params - creates vector<int> with 1 element initialized to specified value value (i.e. 1)
std::vector<int> v{1, 1}; // invocation of constructor with initializer_list<> param - content of initializer_list is copied into vector (i.e. two values of 1)


But following mean something completely different but give same results:

std::vector<int> v(2, 2); // "normal" constructor invocation with (int,int) params - creates vector<int> with 2 elements initialized to specified value value (i.e. 2)
std::vector<int> v{2, 2}; // invocation of constructor with initializer_list<> param - content of initializer_list is copied into vector (i.e. two values of 2)


Note that following will not compile:

std::vector<int> v(1, 1, 1); // no such "normal" constructor

whereas following is completely fine C++11 statement:

std::vector<int> v{1, 1, 1}; // invocation of constructor with initializer_list<> param - content of initializer_list is copied into vector (i.e. three values of 1)


Also note what is perhaps even more surprising that when container value type cannot be initialized from values in the list (no such conversion), then following construction will invoke "normal" constructor:

std::vector<std::string> v{1}; // same as - std::vector<std::string> v(1);


To avoid such behavior assign can be used in initialization (this is still construction, not assignment):

std::vector<std::string> v = {1}; // this will fail to compile
std::vector<int> v = {1}; // this will invoke initializer_list<> constructor as for std::vector<int> v{1};


There is even more about uniform initialization, especially using "auto" keyword - please check e.g. "Effective Modern C++" by Scott Meyers.
Also please check stackoverflow.

poniedziałek, 9 listopada 2015

Thread-safe Catch

Catch test framework is nice, but not thread-safe - see https://github.com/philsquared/Catch/issues/99

There is thread-safe fork of Catch https://github.com/ned14/Catch-ThreadSafe
At the time of writing this original Catch is 1.2.1 whilst thread-safe fork 1.1

piątek, 6 listopada 2015

With or without you (exceptions)

To exception, or not to exception, that is the question.
What about cyclomatic complexity argument? E.g. http://programmers.stackexchange.com/questions/219872

piątek, 28 sierpnia 2015

upgradeable RW-locks - no, no

Never to be forgotten - upgradeable RW-locks always lead to deadlock.
I.e. when reader upgrades to writer without first leaving read lock, then other tread doing same lead to deadlock - no one moves back - deadlock.
Therefore:
- do not think on upgradeable RW-locks,
- if do so, then try-upgrade() function might be good approach, or
- deadlock detection, or
- 3rd user type - beside reader and writer - upgreadeableReader (like EnterUpgradeableReadLock from .NET). There can be only one upgreadeableReader (mutually exclusive). Normal readers cannot upgrade, therefore it is certain that only one user-reader will try to upgrade.

Redirect tcp to console (file) with awk

Sometimes useful, esp. when redirecting local tcp process output to console (file):

BEGIN {
NetService = "/inet/tcp/0/localhost/finger"
print "name" |& NetService
while ((NetService |& getline) > 0)
print $0
close(NetService)
}

More info @http://www.gnu.org/software/gawk/manual/gawkinet/gawkinet.html#Making-Connections