Страничка программиста

В этой статье речь пойдет не о wxWidgets, а о "чистом" C++. А именно о классе, производном от std::bitset. Зачем это мне понадобилось и может понадоить кому-нибудь еще речь пойдет немного позже. А пока я хочу зделать маленькое отступление

Думаю, рано или поздно каждый программист создает свой репозитарий. И приходится думать о том, как удобнее размещать свои наработки для использования в дальнейшем. Я не буду говорить о том, как именно я организовую свое хранилище, но одним из личных правил есть пространство имен, т.е. все свои классы, функции и т.п. я размещаю в одном пространтсве имен, котороя я назвал rpz

Итак, вернемся к предмету статьи, вернее, к причинам, побудившим меня написать свой битовый набор. Как правило, я чаще всего его использую при управлением доступом. По-моему, очень удобно управлять доступом пользователя к пунктам меню, если хранить в базе данных таблицу пользователей и таблицу из которой выгружается меню. Подозреваю, что большинству программистов, пишущих на MFC эта идея, скорее всего, не понравится). Хотя и под этой иблиотекой мне пришлось заниматься такими манипуляциями. Однажды мне пришлось писать программу, используя wxWidgets и СУБД FireBird. В этой среде управление пользователями и доступом к меню писать было гораздо приятнее. Однако более удобной организации доступа к пунктам меню, чем записывать в БД преобразованный битовый набор к числу я не нашел. И тут я столкнулся со следующей трудностью. Битовый набор, предоставляемый STL можно легко инициализировать типом unsigned long или строкой бит. Ну, а если у меня будет больше, чем 32 пункта меню? Почему бы не зделать возможность инициализации битового набора каким-либо другим типом, например __int64? Но ведь пунктов меню в серьезной программе может быть и больше 64-х. Потому и пришла мне мысль написать свой битовый набор, который можно инициализировать не только числовыми типами, но и массивом байт, например. Словом, всем, что приводится к void*).

Теперь о главном. Сами понимаете, что писать подобный класс "с нуля" вещь не очень приятная. А воспользоваться чем-нибудь готовым - милое дело. Потому свое детище, имя которому rpz::BitSet, я произвел от std::bitset. И, как уже, наверное, догадываетесь, это шаблонный класс.


rpzBitSet.h

					
/***************************************************************
 * Name:      rpzBitSet.h
 * Purpose:   BitSet for sizeof(T) > sizeof(long)
 * Author:    developer@sandyhip.net.ua
 * Created:   13/10/2009
 * Copyright: www.sandyhip.net.ua
 * License:   BSD
 **************************************************************/
#ifndef RPZ_BITSET
#define RPZ_BITSET

#include <bitset>
#include <stdexcept>

namespace rpz {
    template<const size_t N, const size_t Nb = 8> class BitSet: public std::bitset<N> {
        /// N - размер в битах
        /// Nb - количество бит в одном байте (8)
    public:
        /// Конструктор по умолчанию
        BitSet() : std::bitset<N> ()
        {
            if (N % Nb != 0) {
                throw std::out_of_range("Size must be a multiple amount bit!");
            }
        }
        /// Конструктор, инициализирующий битовый набор значением
        explicit BitSet(const void* pVal, const size_t nByte) : std::bitset<N> ()
        {
            if (N % Nb != 0) {
                throw std::out_of_range("Size must be a multiple amount bit!");
            }
            try {
                SetValue(pVal,nByte);
            }
            catch (std::exception& e) {
                throw e;
            }
        }
        /// Destructor
        ~BitSet() {}
        /// Функия, инициализирующая битовый набор значением
        void SetValue(const void* pVal, const size_t nByte)
        {
            if (nByte * Nb > N) {
                throw std::out_of_range("Size byte does not correspond to size a bit!");
            }
            char* cByte = new char[nByte];
            memcpy(cByte, pVal, nByte);
            this->reset();
            for (size_t t = 0; t < nByte; t++) {
                for (size_t i = 0, j = Nb * t; i < Nb; i++, j++) {
                    this->set(j, cByte[t] >> i & 0x01);
                }
            }
            delete[] cByte;
        }
        /// Функция, возвращающая значение
        void GetValue(void* pVal, const size_t nByte)
        {
            if (nByte * Nb > N) {
                throw std::out_of_range("Size byte does not correspond to size a bit!");
            }
            char* cByte = new char[nByte];
            for (size_t t = 0; t < nByte; t++) {
                cByte[t] = 0;
                for (size_t i = 0, j = Nb * t; i < Nb; i++, j++) {
                    cByte[t] |= ((*this)[j] << i);
                }
            }
            memcpy(pVal, cByte, nByte);
            delete[] cByte;
        }
        /// Функция, преобразующая строку бит в набор
        void SetString(const char* sVal)
        {
            const size_t nBit = strlen(sVal);
            if (nBit > N) {
                throw std::out_of_range("Size of the bit line out of range!");
            }
            this->reset();
            for (size_t i = 0; i < nBit; i++) {
                this->set((nBit-i)-1, ((sVal[i]=='1') ? true : false));
            }
        }
        /// Функция, возвращающая строку бит
        const char* GetString()
        {
            std::string s = this->template 
                to_string<char,std::char_traits<char>,std::allocator<char> >();
            return s.c_str();
        }
    };
}
#endif // RPZ_BITSET
					
					

Вот и все. Остальной функционал предоставляет базовый класс - std::bitset. Использовать его так:


Exmple.cpp

					
#include <rpzBitSet>
#include <iostream>
/* ... */
unsigned __int64 n = 0;
rpz::BitSet<64> bit;
bit.set();
bit.GetValue(&n,sizeof(n));
std::cout << n << '\n' << bit.GetString() << "\n\n";
if (n != ULONG_LONG_MAX) std::cout << "Error!\n";

int d = 5;
bit.SetValue(&d,sizeof(d));
std::cout << d << '\n' << bit << "\n\n";
/* ... */	
					
					

Пишите автору




rpz::BitSet