Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

ООП, шаблоны и интересная задача

3
14 октября 2004 года
Green
4.8K / / 20.01.2000
В последнее время много обсуждалось (до хрипоты) о разнице в подходах пограммирования на С и С++.
Если есть мысли, высказывайтесь.

А пока предлагаю подключиться к решению задачи, которую подкинул rostyslav (может и сам того не ведая).

Задача такая: реализовать на уровне шаблонов склейку двух строк на этапе компиляции.

Т.е. надо создать шаблонный класс или систему таких классов, которая на входе (параметрами шаблона str1 и str2) будет иметь две строки, а на выходе (видимо в виде строковой константы result) - склеенную строку:
 
Код:
template<str1, str2>
class Cat
{
  ............
  public:
    static const char result[];
};

Тип шаблонных параметров пока не определен, скорее всего это будет ссылка на символьный массив.
Конкретный тип выходного значения тоже не пока определен, но думаю, что это будет константный символьный массив определенной длинны.

В шаблон будут передаваться символьные массивы со внешним связыванием. Иначе, думаю, не получится.

P.S. Перед началом решения такой задачи настоятельно рекомендую прочитать А.Александреску "Modern C++ Design".
492
14 октября 2004 года
alibabaich
238 / / 08.07.2004
Цитата:
Originally posted by Green
В последнее время много обсуждалось (до хрипоты) о разнице в подходах пограммирования на С и С++.
Если есть мысли, высказывайтесь.

А пока предлагаю подключиться к решению задачи, которую подкинул rostyslav (может и сам того не ведая).

Задача такая: реализовать на уровне шаблонов склейку двух строк на этапе компиляции.

Т.е. надо создать шаблонный класс или систему таких классов, которая на входе (параметрами шаблона str1 и str2) будет иметь две строки, а на выходе (видимо в виде строковой константы result) - склеенную строку:
 
Код:
template<str1, str2>
class Cat
{
  ............
  public:
    static const char result[];
};

Тип шаблонных параметров пока не определен, скорее всего это будет ссылка на символьный массив.
Конкретный тип выходного значения тоже не пока определен, но думаю, что это будет константный символьный массив определенной длинны.

В шаблон будут передаваться символьные массивы со внешним связыванием. Иначе, думаю, не получится.

P.S. Перед началом решения такой задачи настоятельно рекомендую прочитать А.Александреску "Modern C++ Design".


Что-нить типа:

Код:
class Cut
{
    Cut *m_Next;
    unsigned int m_Size;
    template < typename T>
        T *m_T;
    void Destroy()
    {
        if(m_Next == NULL)
            delete this;
        else
            m_Next->Destroy();
    }

public:
    Cut()
    {
        m_Next = NULL;
        m_T   = NULL;
        m_Size  = 0;
    }
    Add(T *t,unsigned int size)
    {
        if(m_Next == NULL)
        {
            m_T = t;
            m_Size =0;
        }
        else
        {
            m_Next = new sizeof(Cut);
            m_Next->Add(t,size);
        }
    }
    T* Ret(unsigned int i = 0)
    {
        if(i<=m_Size)
            return (m_T+i);
        else
            return m_Next->Ret(i-m_Size);
    }
    `Cut()
    {
        Destroy();
    }
}

подойдёт?
3
14 октября 2004 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by alibabaich

Что-нить типа:
<skip>
подойдёт?



Не... ты не понял, наверное, суть задачи.
Но все равно, спасибо за участие :)
Жду новых предложений.

Теперь подробнее про задачу.
Есть такой оператор предкомпилятора ## который используется в макросах для склейки строк:

#define cat(a,b) a##b
char str[] = cat("hello ", "world");

Задача сделать что-то типа такого оператора, только на базе шаблонов, который будет выполнятся ещё на стадии компилирования программы.

Практического значения это, наверное, не имеет, но для разминки полезно.

Например такой вот шаблон я написал для преобразования чисел написанных в коде программы в двоичном виде (в коде мы можем писать в десятичном и шестнадцатиричном, а это в двоичном):

Код:
template<unsigned __int64 bin>
class CToDec
{
public:
    enum { value = (bin&1) + 2*CToDec<bin/10>::value };
};

template<>
class CToDec<0>
{
public: enum { value = 0 };
};

#define BIN(bin) CToDec<bin>::value

// пример использования
int i = BIN(01101100);
527
15 октября 2004 года
pavor
275 / / 28.09.2003
Цитата:
Originally posted by Green


Не... ты не понял, наверное, суть задачи.
Но все равно, спасибо за участие :)
Жду новых предложений.

Теперь подробнее про задачу.
Есть такой оператор предкомпилятора ## который используется в макросах для склейки строк:

#define cat(a,b) a##b
char str[] = cat("hello ", "world");

Задача сделать что-то типа такого оператора, только на базе шаблонов, который будет выполнятся ещё на стадии компилирования программы.

Практического значения это, наверное, не имеет, но для разминки полезно.

Например такой вот шаблон я написал для преобразования чисел написанных в коде программы в двоичном виде (в коде мы можем писать в десятичном и шестнадцатиричном, а это в двоичном):
Код:
template<unsigned __int64 bin>
class CToDec
{
public:
    enum { value = (bin&1) + 2*CToDec<bin/10>::value };
};

template<>
class CToDec<0>
{
public: enum { value = 0 };
};

#define BIN(bin) CToDec<bin>::value

// пример использования
int i = BIN(01101100);


Плохой у тебя класс
10^31 много больше, чем 2^64 = 4 * 4^31 - максимальный размер __int64. То есть
поставить единицу в самом старшем бите тебе не удастся. Можно сделать, чтобы брал строку, а не число.

492
15 октября 2004 года
alibabaich
238 / / 08.07.2004
Цитата:
Originally posted by Green


Не... ты не понял, наверное, суть задачи.
Но все равно, спасибо за участие :)
Жду новых предложений.

Теперь подробнее про задачу.
Есть такой оператор предкомпилятора ## который используется в макросах для склейки строк:

#define cat(a,b) a##b
char str[] = cat("hello ", "world");

Задача сделать что-то типа такого оператора, только на базе шаблонов, который будет выполнятся ещё на стадии компилирования программы.

Практического значения это, наверное, не имеет, но для разминки полезно.

Например такой вот шаблон я написал для преобразования чисел написанных в коде программы в двоичном виде (в коде мы можем писать в десятичном и шестнадцатиричном, а это в двоичном):
Код:
template<unsigned __int64 bin>
class CToDec
{
public:
    enum { value = (bin&1) + 2*CToDec<bin/10>::value };
};

template<>
class CToDec<0>
{
public: enum { value = 0 };
};

#define BIN(bin) CToDec<bin>::value

// пример использования
int i = BIN(01101100);


Может я ошибаюсь, но макрос на то и макрос, чтобы Подставлять предпределённый текст. А шаблоны это средство для подстановки кода. То есть, на стадии компиляции будет сгенерированы некие функции и классы исходя из передоваемых им значений. Может я не прав? Но мне кажется, что склейка с помощью шаблонов будет производиться уже на стадии выполнения...

3
15 октября 2004 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by pavor

Плохой у тебя класс


Плохой... сделай лучше.

Цитата:
Originally posted by pavor

10^31 много больше, чем 2^64 = 4 * 4^31 - максимальный размер __int64. То есть
поставить единицу в самом старшем бите тебе не удастся.


Тебе очень надо поставить туда бит? :)

Цитирую себя же: "Практического значения это, наверное, не имеет, но для разминки полезно."

Кроме того, тебе часто приходится писать в коде числовые литералы в двоичном виде длинной 64 цифры?! :D

Хорошо, для тебя сделаю ремарку: передаваемое значение ограничено двадцатью битами.

Цитата:
Originally posted by pavor

Можно сделать, чтобы брал строку, а не число.


Покажи, плз. Это в частности решит поставленную в топике задачу.
Жду от тебя хорошего класса взамен моему плохому.

527
15 октября 2004 года
pavor
275 / / 28.09.2003
Цитата:
Originally posted by Green

Плохой... сделай лучше.


Тебе очень надо поставить туда бит? :)

Цитирую себя же: "Практического значения это, наверное, не имеет, но для разминки полезно."

Кроме того, тебе часто приходится писать в коде числовые литералы в двоичном виде длинной 64 цифры?! :D

Хорошо, для тебя сделаю ремарку: передаваемое значение ограничено двадцатью битами.


Покажи, плз. Это в частности решит поставленную в топике задачу.
Жду от тебя хорошего класса взамен моему плохому.


Можно не класс заменить, а макрос
#define bin32(hi16, lo16) (CToDec(hi16)::value << 16) | CToDec(lo16). На 16 бит запаса хватит :)
В VSDN 2003 просто так невозможно передать константую строку в качестве параметра шаблона. Для этого созданы препоны. Приведи пример хотя бы вызова шаблона
template <const char *str>
class Str
{
};

Str<"str"> и Str<static_cast<const char *>"str"> не работают, в то время как
const char *str = static_cast<const char *>"str";
Отлично работает.
Локальные переменные невозможно передать в качестве параметров шаблона.
Если создать класс
class ConstStr
{
public:
ConstStr(const char *_str) : str(_str) {}
const char *str;
};
И потом использовать его, то он не является compile time evaluatable. Так что используй ##, а еще лучше /**/ - чистый C, будь он не ладен.

3
15 октября 2004 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by pavor

Можно не класс заменить, а макрос
#define bin32(hi16, lo16) (CToDec(hi16)::value << 16) | CToDec(lo16). На 16 бит запаса хватит :)


Все же ты грешил на мой класс.... :)

Цитата:
Originally posted by pavor

В VSDN 2003 просто так невозможно передать константую строку в качестве параметра шаблона. Для этого созданы препоны. Приведи пример хотя бы вызова шаблона
template <const char *str>
class Str
{
};

Str<"str"> и Str<static_cast<const char *>"str"> не работают, в то время как
const char *str = static_cast<const char *>"str";
Отлично работает.
Локальные переменные невозможно передать в качестве параметров шаблона.


Это является особенностью С++ (а не конкретного VC++).
Надо использовать внешнее связыывание:

 
Код:
template <const char *str>
struct Str
{};

const char str[] = "hello world";
Str<str> s;
527
16 октября 2004 года
pavor
275 / / 28.09.2003
Цитата:
Originally posted by Green

Все же ты грешил на мой класс.... :)


Это является особенностью С++ (а не конкретного VC++).
Надо использовать внешнее связыывание:
 
Код:
template <const char *str>
struct Str
{};

const char str[] = "hello world";
Str<str> s;


А кто дает гарантию, что в след версии этого не будет? Все же несколько бредово: я могу передать нулевой указатель и вызвать access violation, но не могу передать указатель на константную строку.
А то, что по крайней мере в других аспектах использования template в этой версии контроль увеличился - это точно. Появились новые ошибки компилятора по этому поводу.
Твой шаблон не работает:
error C2970: 'Str' : template parameter 'str' : 'str' : an expression involving objects with internal linkage cannot be used as a non-type argument
: see declaration of 'Str'
: see declaration of 'str'

3
16 октября 2004 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by pavor

А кто дает гарантию, что в след версии этого не будет?


Для этого есть стандарт.
http://www.csci.csusb.edu/dick/c++std/cd2/template.html#temp.arg

Цитата:

3 A template-argument for a non-type non-reference template-parameter shall be an integral constant-expression of integral type, the name of a non-type non-reference template parameter, the address of an object or a function with external linkage, or a non-overloaded pointer to member.



Цитата:
Originally posted by pavor

Все же несколько бредово: я могу передать нулевой указатель и вызвать access violation, но не могу передать указатель на константную строку.


По поводу нулевого указателя шли споры.
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#354
Думаю, более компетентные люди, чем мы с вами, приняли решение включить это расширение в стандарт.

Цитата:
Originally posted by pavor

Появились новые ошибки компилятора по этому поводу.
Твой шаблон не работает:
error C2970: 'Str' : template parameter 'str' : 'str' : an expression involving objects with internal linkage cannot be used as a non-type argument
: see declaration of 'Str'
: see declaration of 'str'


Сорри, признаю Сказать про внешнее связывание сказал, а вот в коде отметить забыл:

 
Код:
template <const char *str>
struct Str {};

extern const char str[] = "hello world";
Str<str> s;
527
18 октября 2004 года
pavor
275 / / 28.09.2003
Почему такой код компилируется, но при выводе выдает одни 'a'?

template <const char str[], int i, int n>
struct Append
{
Append() {}
~Append() {}

static const char c = str;
Append<str, i + 1, n - 1> astr;
};

template <const char str[], int i>
struct Append<str, i, 0>
{
Append() {}
~Append() {}

static const char c = str;
};

template <const char str[]>
struct Str
{
Str() {}
~Str() {}

Append<str, 0, sizeof(str)> astr;
};
3
19 октября 2004 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by pavor
Почему такой код компилируется, но при выводе выдает одни 'a'?

template <const char str[], int i, int n>
struct Append
{
Append() {}
~Append() {}

static const char c = str;
Append<str, i + 1, n - 1> astr;
};

template <const char str[], int i>
struct Append<str, i, 0>
{
Append() {}
~Append() {}

static const char c = str;
};

template <const char str[]>
struct Str
{
Str() {}
~Str() {}

Append<str, 0, sizeof(str)> astr;
};



Покажи, как ты применяешь свой шаблон.
У тебя это работает?! :o
Дело в том, что такая запись static const char c = str; должна выдавать ошибку, т.к. нельзя использовать str для инициализации константного статического члена.

527
19 октября 2004 года
pavor
275 / / 28.09.2003
Цитата:
Originally posted by Green


Покажи, как ты применяешь свой шаблон.
У тебя это работает?! :o
Дело в том, что такая запись static const char c = str; должна выдавать ошибку, т.к. нельзя использовать str для инициализации константного статического члена.


Использую как ты с "hello world". Компилируется, но не работает.

3
19 октября 2004 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by pavor

Использую как ты с "hello world". Компилируется, но не работает.



Если шаблон не используется, то он не компилируется. Тоже самое относится к методам и статическим членам шаблонного класса.

Видимо, из-за того, что нет обращения к static const char c, у тебя все компилируется.
Если обратиться к этому статическому члену, то появится ошибка компиляции. Ошибка связана с тем, что при инициализации статической константы она должна инициализироваться только интегральным или перечисляемым типом.

Я вижу, что ты организовываешь рекурсию, а вот что происходит в рекурсии не совсем понимаю. Ты присваиваещь (точнее пытаешься, т.к. компилятор этого не позволит) присвоить переменной с каждого этапа рекурсии очередной символ из массива str. Но как ты этим дальше собирался воспользоваться, допустим если бы это получилось? У тебя было бы множество типов на базе Append, каждый из которых соответствовал очередной букве, а как собрать все это вместе?

527
19 октября 2004 года
pavor
275 / / 28.09.2003
Цитата:
Originally posted by Green


Если шаблон не используется, то он не компилируется. Тоже самое относится к методам и статическим членам шаблонного класса.

Видимо, из-за того, что нет обращения к static const char c, у тебя все компилируется.
Если обратиться к этому статическому члену, то появится ошибка компиляции. Ошибка связана с тем, что при инициализации статической константы она должна инициализироваться только интегральным или перечисляемым типом.

Я вижу, что ты организовываешь рекурсию, а вот что происходит в рекурсии не совсем понимаю. Ты присваиваещь (точнее пытаешься, т.к. компилятор этого не позволит) присвоить переменной с каждого этапа рекурсии очередной символ из массива str. Но как ты этим дальше собирался воспользоваться, допустим если бы это получилось? У тебя было бы множество типов на базе Append, каждый из которых соответствовал очередной букве, а как собрать все это вместе?


На самом деле я писал
cout << str.astr.c;
и он выводил 'a'
В al при вызове находился 0.
В общем, если бы работало, то все статические переменные находились бы в памяти подряд (они и находятся, я проверял - адреса &str.astr.c + 1 = &str.astr.astr.c при компиляции, однако в памяти по адресу находится 00h). Тогда можно было бы первую строку перебрать до length1 - 1, вторую до length и был бы concat :) Но по видимому эта задача нерешаема, так как кроме присвоения переменным значений нет способов. Строке ты никак не присвоишь значение на этапе компиляции.

3
19 октября 2004 года
Green
4.8K / / 20.01.2000
Цитата:
Originally posted by pavor

На самом деле я писал
cout << str.astr.c;
и он выводил 'a'
В al при вызове находился 0.
В общем, если бы работало, то все статические переменные находились бы в памяти подряд (они и находятся, я проверял - адреса &str.astr.c + 1 = &str.astr.astr.c при компиляции, однако в памяти по адресу находится 00h). Тогда можно было бы первую строку перебрать до length1 - 1, вторую до length и был бы concat :) Но по видимому эта задача нерешаема, так как кроме присвоения переменным значений нет способов. Строке ты никак не присвоишь значение на этапе компиляции.


М-да... этот способ некорректен, т.к. мы не можем оперировать понятием адреса на таком абстрагированном уровне, как шаблон. Нет никакой гарантии, что компилятор расположит реализации линейно друг за другом, да ещё и в нужной последовательности. Да и с точки зрения языка это совсем неправильно.

А во в том, что задача нерешаема, я с тобой соглашусь. :)

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог