Search code examples
c++private

What's the cleanest way to defeat C++ access qualification?


I decided to work around a bug in GNU libstdc++ by accessing an internal variable. Recalling that Johannes solved the problem on his blog, I checked that out… but couldn't comprehend the code, aside from the basic concept of getting a static initializer to do the dirty work. So, I boiled it down to this, which is pretty compact.

But, as commented, this results in little objects and accessor functions duplicated per translation unit, causing cascade of nasty. Is there a canonical way to do this, say Boost best practice?

Apologies for the bad humor, but it's not gratuitous… we wouldn't want this code to be "safe for work"!

/* This hack installs a static initializer, so to avoid the ordering fiasco,
make one fresh copy per translation unit, via anonymous namespace. */
namespace {

template< typename T, T value, T &dest >
struct class_rape {
    class_rape() { dest = value; } // you've been raped in the class!
    static class_rape r;
};
template< typename T, T value, T &dest >
class_rape< T, value, dest > class_rape< T, value, dest >::r;


// Usage (cvt_[w]filebuf is a specialization of GCC basic_filebuf)

typedef bool cvt_filebuf::*cvt_fb_reading_t;
typedef bool cvt_wfilebuf::*cvt_wfb_reading_t;

/* Access these variables, or functions accessing them (applies recursively),
only in anonymous namespace or in non-header file, per one-definition rule. */
cvt_fb_reading_t cvt_filebuf_reading;
cvt_wfb_reading_t cvt_wfilebuf_reading;

template struct class_rape
    < cvt_fb_reading_t, &cvt_filebuf::_M_reading, cvt_filebuf_reading >;
template struct class_rape
    < cvt_wfb_reading_t, &cvt_wfilebuf::_M_reading, cvt_wfilebuf_reading >;

}

By the way, here is the context: http://pastie.org/1188625.

Update

I solved the duplication issue in my answer below. So now I'm interested in a deterministic, well-defined solution that doesn't involve editing any targeted code and allows hacking multiple specializations of a template at once. (The given hack requires a new instantiation for each target template specialization.)


Solution

  • Bah, it was too late when I posted this… I should've slept on it.

    I can avoid both the static initialization order fiasco and the duplication problem by making the variables truly global, and merely initializing them multiple times. Since the initialization value is the same each time, it doesn't matter when they happen. The first initialization occurs before the first access because initialization appears first in every translation unit where access might occur.

    typedef bool cvt_filebuf::*cvt_fb_reading_t;
    typedef bool cvt_wfilebuf::*cvt_wfb_reading_t;
    
    /* Place accessible variables in global, non-anonymous namespace. */
    cvt_fb_reading_t cvt_filebuf_reading;
    cvt_wfb_reading_t cvt_wfilebuf_reading;
    
    /* This hack installs a static initializer, so to avoid the ordering fiasco,
    make one fresh copy per translation unit, via anonymous namespace. */
    namespace {
    
    template< typename T, T value, T &dest >
    class class_rape { // change access qualification of hack to guarantee ODR, LOL
        class_rape() { dest = value; } // you've been raped in the class!
        static class_rape r;
    };
    template< typename T, T value, T &dest >
    class_rape< T, value, dest > class_rape< T, value, dest >::r;
    
    template struct class_rape
        < cvt_fb_reading_t, &cvt_filebuf::_M_reading, cvt_filebuf_reading >;
    template struct class_rape
        < cvt_wfb_reading_t, &cvt_wfilebuf::_M_reading, cvt_wfilebuf_reading >;
    
    }
    
    /* Accessor functions go here, also outside anonymous namespace. */