Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 31.01.2012, 21:47   #1
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию Данные-члены шаблоны

Дано: Есть класс, имеющий шаблонный метод.

Требуется: в хэдере класса только прототипы методов, реализации - в cpp

Проблема: Другая единица трансляции не видит реализацию шаблонного метода, поскольку эта реализация находится в соседней единице трансляции.

//main.cpp
Код:
#include "CTest.h"

int main() 
{
   CTest test;  int a; 
   test.Foo(a);  //Ошибка линкера:  : error LNK2019: ссылка на неразрешенный внешний символ
                      // "public: void __thiscall CTest::Foo<int>(int &)"
                      // (??$Foo@H@CTest@@QAEXAAH@Z) в функции _main
}
//CTest.h
Код:
#ifndef CTest_h
#define CTest_h

class CTest
{
    public:  template<class T>  void Foo(T& val);
};

#endif
//CTest.cpp
Код:
#include "CTest.h"
#include <iostream>

template<class T> void CTest::Foo(T& val) {   std::cout<< "Победа!\n"; }
Понятно, что если запихать реализацию шаблонного метода вовнутрь самого класса, то проблемы не будет.
Однако, требование к задаче: в хэдере класса только прототипы методов, реализации - в cpp.

Что можно сделать?

Последний раз редактировалось _Bers; 31.01.2012 в 21:51.
_Bers вне форума Ответить с цитированием
Старый 31.01.2012, 21:54   #2
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

Цитата:
Однако, требование к задаче: в хэдере класса только прототипы методов, реализации - в cpp.
Что можно сделать?
1. предугадать все возможные типы, с которыми может использоваться шаблонный метод и явно инстанцировать его
2. сменить язык
3. extern шаблонов. не знаю, что его поддерживает, никогда не использовал (этот пункт только "для галочки")

пс
Цитата:
Понятно, что если запихать реализацию шаблонного метода вовнутрь самого класса, то проблемы не будет.
можно и просто в хидере с объявлением класса. определить метод внутри класса означает сделать его инлайновым
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Последний раз редактировалось pproger; 31.01.2012 в 22:03.
pproger вне форума Ответить с цитированием
Старый 01.02.2012, 13:02   #3
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Задачку решил. Если кому интересно:

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

Суть идеи в том, что потребителю не нужны объекты поставщика услуг в "сферическом вакууме". Ему нужны конкретные услуги, которые он может получить.

Другими словами, Объект Потребителя получает на входе Объект Поставщика услуг, только для того, что бы запустить какие конкретные методы у поставщика услуг.

При этом, не так важно, как именно он это сделает. Главное - что бы это было безопасно, и удобно.

Таким образом, задача по сокрытию деталей реализации обоих классов будет решена, если Потребитель сумеет "сохранить" знание об объекте поставщика, и запустить его методы изнутри своих обычных методов. А обычные методы всегда легко можно упрятать внутри cpp.

Вот так может выглядеть поставщик услуг:
Код:
#ifndef CWorker_h
#define CWorker_h

#include "ConnectContract.h" //набор макросов кодо-генераторов, 
                             //облегчающих процесс создания контрактов классов

//создание коннекторов
Gen_Declarate(void, Work)(Gen_ptr);
Gen_Declarate(int, Work2)(Gen_ptr, int val1, int val2);

Gen_Connector(void, CWorker, Work)(Gen_ptr);
Gen_Connector(int, CWorker, Work2)(Gen_ptr, int val1, int val2);
//////////////////////////////////////////////////////////////////////////


class CWorker
{
public:
    CONTRACT(MYXA);   //заключение контракта. Теперь любые потребители, 
                      //которые работают с данным контрактом, 
                      //будут работать с объектами  этого класса
    
    Gen_GetterAdress(CWorker, Work);  //генерация специальных методов, 
    Gen_GetterAdress(CWorker, Work2); //через которые будет осуществленно взаимодействие с потребителем
public:
    void Work();            
     int Work2(int val1, int val2);    
};
Реализация поставщика услуг. Я постарался сделать так, что бы процесс реализации коннекторов был максимально прост.
Код:
#include "CWorker.h"
#include <iostream>

//реализация коннекторов
Gen_Connector(void, CWorker, Work) (Gen_ptr) 
{
    Gen_Decode(CWorker, Work)();  
}

Gen_Connector(int, CWorker, Work2)(Gen_ptr, int val1, int val2) 
{
    Gen_Decode(CWorker, Work2)(val1, val2); 
}
//////////////////////////////////////////////////////////////////////////

//обычные методы класса
void CWorker::Work() { std::cout<< "Победа!\n"; }
int CWorker::Work2(int val1, int val2) 
{ 
    std::cout<< "Val1 = "<< val1<<"Val2 = "<< val2<<std::endl;
    return val1*2; 
}
_Bers вне форума Ответить с цитированием
Старый 01.02.2012, 13:03   #4
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Вот так может выглядеть потребитель:
Код:
#ifndef CTest_h
#define CTest_h

#include "ConnectContract.h" //набор макросов кодо-генераторов, 
                             //облегчающих процесс создания контрактов классов

class CTest
{
public:
    CTest();
public:
    template<class tAgent>  void Connect(tAgent& val)      
    {   
        CHECK_CONTRACT(val, MYXA);  //проверяет, поддерживает ли поставщик услуг требуемый контракт.
                                    //если нет - будет ошибка компиляции.

        //Допустим, если бы CWorker не поддерживал бы контракт, ошибка выглядела бы так:
        //: error C2039: Error_Contract_MYXA: не является членом "CWorker"

        Gen_InitAgent(mAgent, val);  //взяли адрес объекта

        Gen_m_p(Work)  = val.Gen_GetAdress(Work);  //получили адрес коннектора
        Gen_m_p(Work2) = val.Gen_GetAdress(Work2); 
    }
public:
    void Work(); 
private:

    Gen_pAgent(mAgent); //здесь будет храниться знание об объекте поставщика услуг
    
    Gen_Declarate(void, Work)(Gen_ptr);
    Gen_Declarate(int, Work2)(Gen_ptr, int val1, int val2);

    Gen_MemberPtr(Work);   //пример создания данных-членов, 
    Gen_MemberPtr(Work2);  //которые будут помнить адреса коннекторов
};

#endif
Реализация потребителя:
Код:
#include "CTest.h"
#include <iostream>

CTest::CTest():Gen_InitNullAgent(mAgent),   //используется для безопасности. 
               Gen_InitNullMember(Work),    //Указатель будет инициализирован нулём
               Gen_InitNullMember(Work2) {} //При попытки обращения к нулевым указателям, 
                                            //сработает ассерт

void CTest::Work() 
{
    Gen_Use(Work)(mAgent);          //пример запуска методов поставщика-услуг
    Gen_Use(Work2)(mAgent, 33,44);  //из обычных методов класса
}
//Использование:
Код:
#include "CWorker.h"
#include "CTest.h"

int main()
{
    CWorker worker; 
    CTest test;
    test.Connect(worker); //потребитель и поставщик установили связь
    test.Work();              //потребитель запускает методы поставщика
}
Таким образом получаем следующий профит:
1. Классы абсолютно никак не связанны. Не имеют никаких общих точек соприкосновения. Абсолютно ничего не знают друг о друге.

2. Между собой могут взаимодействовать абсолютно любые классы, если они придерживаются некоторого контракта. Контроль за этим осуществляется на этапе компиляции

3. Потребитель имеет только один не скрытый метод, который ничего интересного не расскажет. Все остальное теперь легко можно прятать в cpp

Последний раз редактировалось _Bers; 01.02.2012 в 13:05.
_Bers вне форума Ответить с цитированием
Старый 01.02.2012, 13:17   #5
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2_Bers
какая-то отвратительная реализация Наблюдателя
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance
pproger вне форума Ответить с цитированием
Старый 01.02.2012, 13:18   #6
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от pproger Посмотреть сообщение
2_Bers
какая-то отвратительная реализация Наблюдателя
Предложи лучше. Учитывая требования к задачи.
_Bers вне форума Ответить с цитированием
Старый 01.02.2012, 13:22   #7
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2_Bers
что тут предлагать то. открой банду четырех и посмотри на классическую реализацию
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance
pproger вне форума Ответить с цитированием
Старый 01.02.2012, 13:40   #8
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от pproger Посмотреть сообщение
2_Bers
что тут предлагать то. открой банду четырех и посмотри на классическую реализацию
То есть, требования к решению задачи можно проигнорировать, да?
_Bers вне форума Ответить с цитированием
Старый 01.02.2012, 14:11   #9
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2_Bers
какие требования? это требование?
Цитата:
задача по сокрытию деталей реализации обоих классов
и в чем проблема с классическим обсервером?

ДОБАВЛЕНО:
и да
Цитата:
Gen_Declarate(void, Work)(Gen_ptr);
что это вообще такое?
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Последний раз редактировалось pproger; 01.02.2012 в 14:28.
pproger вне форума Ответить с цитированием
Старый 01.02.2012, 15:27   #10
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от pproger Посмотреть сообщение
2_Bers
какие требования? это требование?
Дано: Есть класс, имеющий шаблонный метод.

Требуется: в хэдере класса только прототипы методов, реализации - в cpp


Цитата:
Сообщение от pproger Посмотреть сообщение
и в чем проблема с классическим обсервером?
У меня нет проблем с классическим обсервером.
Просто он не подходит для решения данной задачи
_Bers вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Разнести данные в заготовленные шаблоны по критериям Proha Microsoft Office Excel 5 02.03.2011 21:22
Статические члены класса _-Re@l-_ Общие вопросы C/C++ 8 20.01.2011 23:18
члены последовательности amikulia Помощь студентам 0 14.01.2011 12:35
наибольший и последний члены Zhasik Паскаль, Turbo Pascal, PascalABC.NET 1 27.12.2010 08:53
Static члены assasin Общие вопросы C/C++ 2 21.08.2009 10:54