ООП, шаблоны и интересная задача
Если есть мысли, высказывайтесь.
А пока предлагаю подключиться к решению задачи, которую подкинул rostyslav (может и сам того не ведая).
Задача такая: реализовать на уровне шаблонов склейку двух строк на этапе компиляции.
Т.е. надо создать шаблонный класс или систему таких классов, которая на входе (параметрами шаблона str1 и str2) будет иметь две строки, а на выходе (видимо в виде строковой константы result) - склеенную строку:
class Cat
{
............
public:
static const char result[];
};
Тип шаблонных параметров пока не определен, скорее всего это будет ссылка на символьный массив.
Конкретный тип выходного значения тоже не пока определен, но думаю, что это будет константный символьный массив определенной длинны.
В шаблон будут передаваться символьные массивы со внешним связыванием. Иначе, думаю, не получится.
P.S. Перед началом решения такой задачи настоятельно рекомендую прочитать А.Александреску "Modern C++ Design".
В последнее время много обсуждалось (до хрипоты) о разнице в подходах пограммирования на С и С++.
Если есть мысли, высказывайтесь.
А пока предлагаю подключиться к решению задачи, которую подкинул rostyslav (может и сам того не ведая).
Задача такая: реализовать на уровне шаблонов склейку двух строк на этапе компиляции.
Т.е. надо создать шаблонный класс или систему таких классов, которая на входе (параметрами шаблона str1 и str2) будет иметь две строки, а на выходе (видимо в виде строковой константы result) - склеенную строку:
class Cat
{
............
public:
static const char result[];
};
Тип шаблонных параметров пока не определен, скорее всего это будет ссылка на символьный массив.
Конкретный тип выходного значения тоже не пока определен, но думаю, что это будет константный символьный массив определенной длинны.
В шаблон будут передаваться символьные массивы со внешним связыванием. Иначе, думаю, не получится.
P.S. Перед началом решения такой задачи настоятельно рекомендую прочитать А.Александреску "Modern C++ Design".
Что-нить типа:
{
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();
}
}
подойдёт?
Что-нить типа:
<skip>
подойдёт?
Не... ты не понял, наверное, суть задачи.
Но все равно, спасибо за участие :)
Жду новых предложений.
Теперь подробнее про задачу.
Есть такой оператор предкомпилятора ## который используется в макросах для склейки строк:
#define cat(a,b) a##b
char str[] = cat("hello ", "world");
Задача сделать что-то типа такого оператора, только на базе шаблонов, который будет выполнятся ещё на стадии компилирования программы.
Практического значения это, наверное, не имеет, но для разминки полезно.
Например такой вот шаблон я написал для преобразования чисел написанных в коде программы в двоичном виде (в коде мы можем писать в десятичном и шестнадцатиричном, а это в двоичном):
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);
Не... ты не понял, наверное, суть задачи.
Но все равно, спасибо за участие :)
Жду новых предложений.
Теперь подробнее про задачу.
Есть такой оператор предкомпилятора ## который используется в макросах для склейки строк:
#define cat(a,b) a##b
char str[] = cat("hello ", "world");
Задача сделать что-то типа такого оператора, только на базе шаблонов, который будет выполнятся ещё на стадии компилирования программы.
Практического значения это, наверное, не имеет, но для разминки полезно.
Например такой вот шаблон я написал для преобразования чисел написанных в коде программы в двоичном виде (в коде мы можем писать в десятичном и шестнадцатиричном, а это в двоичном):
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. То есть
поставить единицу в самом старшем бите тебе не удастся. Можно сделать, чтобы брал строку, а не число.
Не... ты не понял, наверное, суть задачи.
Но все равно, спасибо за участие :)
Жду новых предложений.
Теперь подробнее про задачу.
Есть такой оператор предкомпилятора ## который используется в макросах для склейки строк:
#define cat(a,b) a##b
char str[] = cat("hello ", "world");
Задача сделать что-то типа такого оператора, только на базе шаблонов, который будет выполнятся ещё на стадии компилирования программы.
Практического значения это, наверное, не имеет, но для разминки полезно.
Например такой вот шаблон я написал для преобразования чисел написанных в коде программы в двоичном виде (в коде мы можем писать в десятичном и шестнадцатиричном, а это в двоичном):
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. То есть
поставить единицу в самом старшем бите тебе не удастся.
Тебе очень надо поставить туда бит? :)
Цитирую себя же: "Практического значения это, наверное, не имеет, но для разминки полезно."
Кроме того, тебе часто приходится писать в коде числовые литералы в двоичном виде длинной 64 цифры?! :D
Хорошо, для тебя сделаю ремарку: передаваемое значение ограничено двадцатью битами.
Можно сделать, чтобы брал строку, а не число.
Покажи, плз. Это в частности решит поставленную в топике задачу.
Жду от тебя хорошего класса взамен моему плохому.
Плохой... сделай лучше.
Тебе очень надо поставить туда бит? :)
Цитирую себя же: "Практического значения это, наверное, не имеет, но для разминки полезно."
Кроме того, тебе часто приходится писать в коде числовые литералы в двоичном виде длинной 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, будь он не ладен.
Можно не класс заменить, а макрос
#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";
Отлично работает.
Локальные переменные невозможно передать в качестве параметров шаблона.
Это является особенностью С++ (а не конкретного VC++).
Надо использовать внешнее связыывание:
struct Str
{};
const char str[] = "hello world";
Str<str> s;
Все же ты грешил на мой класс.... :)
Это является особенностью С++ (а не конкретного VC++).
Надо использовать внешнее связыывание:
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'
А кто дает гарантию, что в след версии этого не будет?
Для этого есть стандарт.
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.
Все же несколько бредово: я могу передать нулевой указатель и вызвать access violation, но не могу передать указатель на константную строку.
По поводу нулевого указателя шли споры.
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#354
Думаю, более компетентные люди, чем мы с вами, приняли решение включить это расширение в стандарт.
Появились новые ошибки компилятора по этому поводу.
Твой шаблон не работает:
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'
Сорри, признаю Сказать про внешнее связывание сказал, а вот в коде отметить забыл:
struct Str {};
extern const char str[] = "hello world";
Str<str> s;
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;
};
Почему такой код компилируется, но при выводе выдает одни '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 для инициализации константного статического члена.
Покажи, как ты применяешь свой шаблон.
У тебя это работает?! :o
Дело в том, что такая запись static const char c = str; должна выдавать ошибку, т.к. нельзя использовать str для инициализации константного статического члена.
Использую как ты с "hello world". Компилируется, но не работает.
Использую как ты с "hello world". Компилируется, но не работает.
Если шаблон не используется, то он не компилируется. Тоже самое относится к методам и статическим членам шаблонного класса.
Видимо, из-за того, что нет обращения к static const char c, у тебя все компилируется.
Если обратиться к этому статическому члену, то появится ошибка компиляции. Ошибка связана с тем, что при инициализации статической константы она должна инициализироваться только интегральным или перечисляемым типом.
Я вижу, что ты организовываешь рекурсию, а вот что происходит в рекурсии не совсем понимаю. Ты присваиваещь (точнее пытаешься, т.к. компилятор этого не позволит) присвоить переменной с каждого этапа рекурсии очередной символ из массива str. Но как ты этим дальше собирался воспользоваться, допустим если бы это получилось? У тебя было бы множество типов на базе Append, каждый из которых соответствовал очередной букве, а как собрать все это вместе?
Если шаблон не используется, то он не компилируется. Тоже самое относится к методам и статическим членам шаблонного класса.
Видимо, из-за того, что нет обращения к 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 :) Но по видимому эта задача нерешаема, так как кроме присвоения переменным значений нет способов. Строке ты никак не присвоишь значение на этапе компиляции.
На самом деле я писал
cout << str.astr.c;
и он выводил 'a'
В al при вызове находился 0.
В общем, если бы работало, то все статические переменные находились бы в памяти подряд (они и находятся, я проверял - адреса &str.astr.c + 1 = &str.astr.astr.c при компиляции, однако в памяти по адресу находится 00h). Тогда можно было бы первую строку перебрать до length1 - 1, вторую до length и был бы concat :) Но по видимому эта задача нерешаема, так как кроме присвоения переменным значений нет способов. Строке ты никак не присвоишь значение на этапе компиляции.
М-да... этот способ некорректен, т.к. мы не можем оперировать понятием адреса на таком абстрагированном уровне, как шаблон. Нет никакой гарантии, что компилятор расположит реализации линейно друг за другом, да ещё и в нужной последовательности. Да и с точки зрения языка это совсем неправильно.
А во в том, что задача нерешаема, я с тобой соглашусь. :)