3

I'm developing a generic Genetic Algorithm library, where the chromosome of each organism is its bit representation in memory. So, for instance, if I want to mutate a organism, I flip the bits themselves of the object randomly.

At first, I tried using the bitset class from the C++ standard library, but, when converting back to an object T, my only option was using the to_ullong member function, which was a problem for representations with a number of bits larger than the size of an unsigned long long.

Then I decided to create a generic library for bitwise operations on any object T, so I could apply these operations directly onto the objects themselves, instead of converting them first to a bitset.

So you can see what I'm trying to achieve, here's a function from the library:

template<typename T>
void flip(T& x, size_t const i)
{
    x ^= 1 << i;
}

And it's used in the GA library like this:

template<typename T>
void GeneticAlgorithm<T>::mutate(T& organism, double const rate)
{
    std::random_device rd;
    std::mt19937 mt(rd());

    std::uniform_real_distribution<double> dist(0, 1);

    for(size_t i = 0; i < m_nBits; ++i)
        if(dist(mt) <= rate)
            bit::flip(organism, i);
}

It would be really nice if this worked, however now I'm getting this error message from the VC++ 2015 RC compiler:

Severity Code Description Project File Line Error C2677 binary '^': no global operator found which takes type 'T' (or there is no acceptable conversion) GeneticAlgorithm path\geneticalgorithm\geneticalgorithm\BitManip.hpp 57

If I correct this error for the ^, I get more for the other operators.

I haven't used bitwise operators before in my code, so I guess these operators are not supposed to be used with any object? If so, how could I work around the problem?

seaotternerd
  • 6,298
  • 2
  • 47
  • 58
  • 3
    From almost all of the [bitwise operators](https://stackoverflow.com/questions/14260204/what-is-the-definition-of-bitwise-operators-in-c) defined in the C++ standard, they end the description with *"The operator applies only to integral or unscoped enumeration operands"* – Cory Kramer Jul 13 '15 at 19:33
  • 4
    Also C++ and C are different languages. Do not tag C in your question, especially since you have templates and C++ Standard Library classes. – Cory Kramer Jul 13 '15 at 19:34
  • 1
    You can always cast the address of an organism or whatever to char* , compute the byte concerned and the bit offset in it, and invert _that_ bit in _that_ byte. – Peter - Reinstate Monica Jul 13 '15 at 19:36
  • My gut feeling is also that flip() should be a member function of organism. Maybe you'll use something different than bits in the future (imagine you want to count flips occured per gene or so). You will want to perform the flip without exactly knowing how the organism handles it. Hell, you might invoke a remote procedure with large populations. – Peter - Reinstate Monica Jul 13 '15 at 19:40
  • @CoryKramer, so that's the explanation. As for it being a C++ question, I put C as a tag because my problem was more related to bitwise operations that it was to templates and the standard library. – Felipe Canever Jul 13 '15 at 20:01
  • 2
    @FelipeCanever *"I put C as a tag because my problem was more related to bitwise operations that it was to templates and the standard library."* They are still different languages. You cannot assume that everything that holds true in C also holds true in C++. Operators don't always behave the same in the two languages. – cdhowie Jul 13 '15 at 20:08
  • The approach suggested by @Peter will lead to extremely sad times if you have e.g. pointer members in your struct (or implicit ones, e.g. it's a polymorphic type). Other than that, it should work (other than technically you would be invoking UB by manipulating padding bits, etc.) – Oliver Charlesworth Jul 14 '15 at 12:11
  • @Oliver I hope the OP is not doing this with polymorphic types! I very generally hope that he doesn't have any meaningful members in it. I assumed that he considers his objects large bit fields. That said, the OP's approach is likely unsound; any "organism" should not be treated like a bit field but expose a member function to "flip" a "trait", leaving the implementation of traits opaque and making organisms fully-featured classes with extensible properties and operations on them. – Peter - Reinstate Monica Jul 14 '15 at 14:22
  • @PeterSchneider Yes, I know I have a huge problem. By flipping the bits of a generic type, I can potentially break a class invariant. – Felipe Canever Jul 15 '15 at 06:48

2 Answers2

2

What you want to achieve can be done like that (see Peter Schneider's comment):

template<typename T> void flip(T& x, size_t const i) {
    unsigned char* data = reinterpret_cast<unsigned char*>(&x);
    data[i/8] ^= (1 << (i%8));
}

what it does is reinterpreting your data x as an array of bytes (unsigned char), then determining which byte should be flipped (i/8), then which bit within the byte (i%8).

Note: in addition, it may be safe to add at the beginning of the function:

assert(i < sizeof(T)*8)
BrunoLevy
  • 2,495
  • 17
  • 30
1

I am under the impression that you are not yet fully appreciating the object oriented features C++ offers. (That's not untypical when coming from a more data-centric programming in C. C++ is specifically designed to make that transition at the desired speed and to make it painless.)

My suggestion is to encapsulate the flip operation in an organism and let the organism handle it. As an illustration (untested, but compiles):

#include<climits>  // CHAR_BIT
#include<cstdlib>  // exit()

class string;
void log(const char *);

// inaccessible from the outside
constexpr int NUM_TRAITS = 1000;
constexpr size_t TRAIT_ARR_SZ = (NUM_TRAITS+CHAR_BIT-1)/CHAR_BIT;

class Organism
{ 

    char traits[TRAIT_ARR_SZ];
    int flips[NUM_TRAITS];

    /////////////////////////////////////////////////////////////
    public:

    Organism()  {  /* set traits and flips zero */  }

    // Consider a virtual function if you may derive 
    /** Invert the trait at index traitIndex */
    void flipTrait(int traitIndex)
    {
        if( traitIndex >= NUM_TRAITS ) { log("trait overflow"); exit(1); }

        int charInd = traitIndex / CHAR_BIT;
        int bitInd = traitIndex % CHAR_BIT;

        traits[traitIndex] ^= 1 << bitInd;
        flips[traitIndex]++;
    }

    // Organisms can do so much more!
    void display();
    void store(string &path);
    void load(string &path);
    void mutate(float traitRatio);
    Organism clone();
};
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • I almost always programmed solely in C++ and I decided to make a generic function library for quick GA protoyping in structs. Now that you have pointed out, I think I will add an Organism class from which one can inherit. Thanks. – Felipe Canever Jul 15 '15 at 06:58