// расшаренный .h
template<typename> class CTemplate;
class CMyInt
{
public:
CMyInt();
~CMyInt();
private:
CTemplate<int>* pImpl;
};
Сокрытие реализации шаблонного класса
Можно ли каким-нибудь образом скрыть реализацию шаблонного класса от пользователя, если известно, что подставляться будут только базовые типы (список заранее известен). То есть чтобы можно было выдать объектный файл/библиотеку, заголовочные файлы и некто другой всё это успешно мог использовать в своем проекте?
Поробуй реализацию написать с использованием void*, а в Шаблонах допиши приведение типов. У Страуструпа во "Введении в язык С++"(2е издание) такой прием описан.
[/QUOTE]
Нельзя.
[QUOTE=tarekon]То есть чтобы можно было выдать объектный файл/библиотеку, заголовочные файлы и некто другой всё это успешно мог использовать в своем проекте?[/QUOTE]
Тогда шаблоны перестают иметь смысл.
Кстати, а как ты предполагаешь скрывать реализацию НЕшаблонного класса от пользователя? Предоставляя ему только базовый интерфейс и фабрику объектов?
Тогда какая разница (для пользователя) шаблонные классы или нет если у них единый базовый интерфейс.
[QUOTE=dr.Chaos]
Поробуй реализацию написать с использованием void*, а в Шаблонах допиши приведение типов.
[/QUOTE]
Ни в коем случае так не делай. Не за чем лишний раз генерить небезопасный код.
Кстати, а как ты предполагаешь скрывать реализацию НЕшаблонного класса от пользователя?[/QUOTE]
Пардон, возможно, неправильно выразился. Имелось в виду скрыть от программиста детали работы функций - алгоритмы, воплощение в коде. То есть если я сделал класс (два файла - cpp и h), то я могу его откомпилировать и отдать другому человеку уже объектный файл и заголовочный (obj и h) и прочитать мой код он уже не сможет (верно?). Аналогично и с шаблонными классами: как можно заставить компилятор сделать подстановку всех возможных значений параметра и сгенерировать объектный файл, чтобы потом отдать его в чужие руки. Не заморачиваясь на фабрики класса.
Код:
Код:
// .cpp находящийся в библиотеке
CMyInt::CMyInt() {
pImpl = new CTemplate<int>;
}
CMyInt::~CMyInt() {
delete pImpl;
}
CMyInt::CMyInt() {
pImpl = new CTemplate<int>;
}
CMyInt::~CMyInt() {
delete pImpl;
}
ну и т.д. для всех остальных типов.
Ни в коем случае так не делай. Не за чем лишний раз генерить небезопасный код.[/QUOTE]
Так шаблоны как раз и делают его типобезопасным.
Они были в общем так и задуманы создателем языка, ведь при этом варианте дублируется только код который обеспечивает безопастность типов. Конечно этот вариант тайт в себе опастности, но это вполне приемлемая цена за библиотеку шаблонов с закрытым кодом.
Код:
// расшаренный .h
template<typename> class CTemplate;
class CMyInt
{
public:
CMyInt();
~CMyInt();
private:
CTemplate<int>* pImpl;
};
template<typename> class CTemplate;
class CMyInt
{
public:
CMyInt();
~CMyInt();
private:
CTemplate<int>* pImpl;
};
Код:
// .cpp находящийся в библиотеке
CMyInt::CMyInt() {
pImpl = new CTemplate<int>;
}
CMyInt::~CMyInt() {
delete pImpl;
}
CMyInt::CMyInt() {
pImpl = new CTemplate<int>;
}
CMyInt::~CMyInt() {
delete pImpl;
}
ну и т.д. для всех остальных типов.[/QUOTE]
Допустим, у меня в шаблонном классе энное количество функций. Разве, увидев только описание указателя на CTemplate<int>, компилятор сгенерит все соответствующие этому шаблону фукнции? По-моему, нет: компилер делает подстановку только тех функций, которые реально вызываются, а на остальные кладет. Соответственно фраза
Код:
pImpl = new CTemplate<int>
Но с этим примером надо еще разобраться. Попробую сделать - скажу - был прав или нет.
КОНЕЧНО, надо сделать все переходники для всех нужных методов.
КОНЕЧНО, надо сделать все переходники для всех нужных методов.[/QUOTE]
Именно!!!!!! А вот как бы схалявить...
Код:
// templ.h
template< typename T >
class B {
public:
T getElem();
private:
T perem;
};
// templ.cpp
#include "templ.h"
template<typename T>
T B<T>::getElem()
{
return perem;
}
template class B<int>;
// main.cpp
#include "templ.h"
#include <iostream>
B<int> cla;
void main()
{
std::cout << cla.getElem() << std::endl;
}
template< typename T >
class B {
public:
T getElem();
private:
T perem;
};
// templ.cpp
#include "templ.h"
template<typename T>
T B<T>::getElem()
{
return perem;
}
template class B<int>;
// main.cpp
#include "templ.h"
#include <iostream>
B<int> cla;
void main()
{
std::cout << cla.getElem() << std::endl;
}
Сие чудо компилится и работает. Очень удобно, без всяких переходников. Рекомендую! :)
Хм... интересная конструкция, напоминает частную специализацию, но это не она.
создаёш фаил:
оформляеш класс который хочеш скрыть.
создаёш ёщо один фаил:
обявляеш в нём этот класс,
просто class classname;
и в main фаиле делаеш include обоех фаилов
Ты сам то слышал звон, да не знаешь где он.
Во первых, то что ты описал бред какой-то, ничего не дающий вообще. Если я определяю класс в одном файле и подключаю его к другому файлу, то зачем нужен подключаемый третий файл с одним объявлением?
Во вторых, прокси - это не то, что нужно автору. Нагрузка у прокси совсем иная, а инкапсуляция в нем не самоцель.
Ну и в третьих, где шаблоны?
Способа 2:
1) создать класс содержащий указатель на скрываемый и переопределить в нём operator-> чтобы все вызовы переходили на хранящийся внутри объект класса - это умный указатель. Собственно это не совсем то, но может пригодится.
2) создать класс содержащий указатель на скрываемый и определить в нём функции, которые будут вызывать функции хранящегося указателя на объект. Таким образом юзер видит только те функции, которые мы хотим чтобы он видел, и необязательно под теми же именами, что и в скрываемом классе. Также никто не мешает добавить парочку лишних.
Выглядеть будет так:
"WantToHide.h"
class WantToHide
{
friend class IntefacePointer; // друзьями можно указывать даже то, чего нет вовсе...
...
void F(double x);
};
"InterfacePointer.h"
class WantToHide;
class InterfacePointer
{
WantToHide *ptr;
public:
void f(double x);
};
"interfacepointer.cpp"
// Клиентам не полагается видеть .cpp файлы.
// Если сильно хочется - есть шифровка и Custom Build Step для
// расшифровки на время билда и удаления рассшифровок после.
#include "WantToHide.h"
InterfacePointer::InterfacePointer()
{
ptr = new WantToHide;
};
void InterfacePointer::f(double x)
{
ptr->F(x);
};
InterfacePointer::~InterfacePointer()
{
delete ptr;
}
Вот так. Скрываемый класс запрятан в свой .cpp, который и зашифровать можно. Желающие его использовать просто юзают InterfacePointer, он полностью заменяет собой скрываемый класс, и при этом не раскрывает никаких секретов.
Библиотечный вариант сложнее и накладывает ограничения так как скрываемый класс может быть засунут в библиотеку с другими настройками компилятора - и быть на чуть-чуть другом железе или разных настройках компилятора небезопасным. При компиляции вместе с остальными проектом он гораздо переносимее.
P.S. забыл о шаблонах - но собственно разницы никакой. Интерфейсный указатель тоже будет шаблонным, и создавать базовый класс по шаблону.
template <typename T> InteracePointer::InterfacePointer()
{
ptr = new WantToHide<T>;
};
Всё.
Собственно это уже обсуждалось и автора топика не устроило.
может более продвинутые люди ещё както используют его.
но определение того proxy которое я написа не я придумал, я пока что учусь у дейтела и доверяю его терминам.
Т.е. он имеет такой же интерфейс, как и у замещаемого объекта, но несколько другую реализацию.
Как пример, есть некоторый очень ресурсоемкий объект, создавать который накладно, а необходимость в нем невсегда присутствует, однако он должен быть готов в случае обращения к нему. Здесь и применяется прокси, который выглядит для пользователя, как оригинальный объект, но не занимает ресурсов. В случае обращения к прокси (естественно пользователь думает, что обращается к оригинальному объекту), в случае необходимости создается этот ресурсоемкий объект и вызов передается ему. Например, есть граф, узлы которого весьма тяжелые. Без узлов графа не будет, но конструировать все узлы весьма проблематично. Здесь и может быть применен прокси, который при изменении топологии и подобных операциях справится и сам, а для специфичных для узла операциях будет создавать реальные объекты по мере надобности.
На VC 2005? А на 2003 сработает? Или это в ANSI-стандарте дано? И что это означает - форсировать компилятор сгенерить реализацию шаблона конкретно для int и поместить ее в obj? Если так, то это действительно обалденное открытие :) надо нобелевскую давать :)
Ну так проверь и найди ответ в стандарте.
За последнее буду признателен.
За последнее буду признателен.[/QUOTE]
Что говорит ANSI.
http://www.csci.csusb.edu/dick/c++std/cd2/template.html#temp
Во-первых:
template-declaration:
exportopt template < template-parameter-list > declaration
An exported template need only be declared (and not necessarily defined) in a translation unit in which it is instantiated.
A non-inline template function or a static data member template is called an exported template if its definition is preceded by the keyword export or if it has been previously declared using the keyword export in the same translation unit.
Templates defined in an unnamed namespace shall not be exported.
Теперь по-русски.
Насколько я понимаю, стандарт позволяет экспортировать шаблоны в другие модули, для этого их следует описывать с ключевым словом export, с тем ограничением, что шаблон не может быть описан в глобальном пространстве имён.
Во-вторых.
http://www.csci.csusb.edu/dick/c++std/cd2/template.html#temp.explicit глава 14.7.2 "Explicit instantiation". Из названия предполагаем, что можно явным образом заставить компилятор сгенерить код для конкретной реализации шаблона. Читаем примеры:
Код:
[Example:
template<class T> class Array { /* ... */ };
template class Array<char>;
template<class T> void sort(Array<T>& v) { /* ... */ }
template void sort(Array<char>&); // argument is deduced here
namespace N {
template<class T> void f(T&) { }
}
template void N::f<int>(int&);
--end example]
template<class T> class Array { /* ... */ };
template class Array<char>;
template<class T> void sort(Array<T>& v) { /* ... */ }
template void sort(Array<char>&); // argument is deduced here
namespace N {
template<class T> void f(T&) { }
}
template void N::f<int>(int&);
--end example]
К сожалению, это "всего лишь" стандарт, не все компиляторы могут это поддерживать. Например, MSDN пишет, что VC++ 6.0 не поддерживал partial specialization, это появилось в VC 2003. А насчет explicit instantiation - не знаю.
Насколько я понимаю, стандарт позволяет экспортировать шаблоны в другие модули, для этого их следует описывать с ключевым словом export, с тем ограничением, что шаблон не может быть описан в глобальном пространстве имён.
[/QUOTE]
Стандарт то позволяет, только вот txport поддерживает всего пара компиляторов и то в весьма примитивных случаях. Так что можно считать, что это несбыточные мечты. Есть предположения, что export вовсе уберут из стандарта.
[QUOTE=cheburator]
Во-вторых.
http://www.csci.csusb.edu/dick/c++std/cd2/template.html#temp.explicit глава 14.7.2 "Explicit instantiation". Из названия предполагаем, что можно явным образом заставить компилятор сгенерить код для конкретной реализации шаблона. Читаем примеры:
Код:
[Example:
template<class T> class Array { /* ... */ };
template class Array<char>;
template<class T> void sort(Array<T>& v) { /* ... */ }
template void sort(Array<char>&); // argument is deduced here
namespace N {
template<class T> void f(T&) { }
}
template void N::f<int>(int&);
--end example]
template<class T> class Array { /* ... */ };
template class Array<char>;
template<class T> void sort(Array<T>& v) { /* ... */ }
template void sort(Array<char>&); // argument is deduced here
namespace N {
template<class T> void f(T&) { }
}
template void N::f<int>(int&);
--end example]
[/QUOTE]
А вот это уже в тему.
[QUOTE=cheburator]
К сожалению, это "всего лишь" стандарт, не все компиляторы могут это поддерживать. Например, MSDN пишет, что VC++ 6.0 не поддерживал partial specialization, это появилось в VC 2003. А насчет explicit instantiation - не знаю.[/QUOTE]
VC++ 6.0 вообще очень плохо поддерживает шаблоны и не только частичную специализацию, на частной специализации он тоже косячил, не поддерживает шаблонные параметры шаблонов и т.д.