Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> библиотекa распределенного общения, детали разработки 
:(
    Опции темы
mes
  Дата 27.2.2011, 18:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



просьба эту тему  не засорять, чтоб не было разрыва между фрагментами кода.. 
вопросы для обсуждения задавать в соседней теме:
вопросы, предложения и обсуждение


тема создана как ветвь в продолжение одной сильно разросшейся темы:
http://forum.vingrad.ru/forum/topic-311688.html
в которой по-моему уже никто не ориентируется.. 
В ней обсуждались разные приемы для реализация прослойки для распределенной коммуникации.

В этой теме я попытаюсь определить сущности и собрать приемы помогающие реализовать подобное.. 
Т.е. другими словами хочу выразить свой взгляд на проблему.. 

легенда используемыx ниже терминов :

type   -  тип вызова
migrant -  сериализованный пакет аргументов
i_invoke -  интерфейс выполнения для  мигранта 
channel - передача мигранта внутри и за границы приложения
map    - карта соответствий ид и инвокера 
mapper  - передача мигранта конкретному инвокеру из ассоциированной карты..
function_cb - шаблон callback`a основаннoго на function
signal_cb- шаблон callback`a основаннoго на signal
и так поехали..

Это сообщение отредактировал(а) mes - 28.2.2011, 12:39


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 19:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



изначально коды будут приводиться с допущениями выражая только основную суть и не акцептируя внимание на мелочах.. 

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

template<typename F> 
struct type
{
   const char * id;
};


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

struct migrant {
   std::string _id;
   std::string _raw;
};


при получении мигранта ,надо его выполнить..
заведем интерфейс для этого :
Код

struct i_invoker 
{
   virtual void operator () (migrant const&) =0;
};

для начала реализуем шаблон инвокера на основе std::function, хотя стратегию можно будет выбирать любую..(например boost::signal)
Код

template<typename F>
struct invoker : i_invoker 
{
   virtual void operator () (migrant const&) {}
   std::function<F> _function;
};



Это сообщение отредактировал(а) mes - 28.2.2011, 04:07


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 19:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



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

struct channel
{
    channel& operator <<(migrant const&) {..}
};
 
отправлять мы теперь можем., но нм нужно еще получать.. 
для того чтоб выбрать по имени нужный инвокер нужна карта, но
Код

std::map<std::string, std::shared_ptr<i_invoker> > 

перекладывает гарантию типобезопасности на программиста.. 



--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 20:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



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

struct map 
{
   template<class F>
   std::function<F>& operator[] (type<F> const& t) { 

     i_invoker * inv;
     items::iterator it = _items.find(t.id);
     if (it ==_items.end())
     {
          inv   = new invoker<F>;
         _items[t.id] = invoker_ptr(inv);
     }
     else  inv = it->second.get();
     
     return ( static_cast<invoker<F>*>(inv)->_function);
     
   }
   typedef std::shared_ptr<i_invoker> invoker_ptr;
   typedef std::map<std::string, invoker_ptr> items;
   
   items _items;
};


Добавлено через 4 минуты и 58 секунд
напишем пример по использованию :
Код

namespace dy{
// тут вышеупомянуты типы
} // namespace dy

// определение протокола :

dy::type<void ( std::string
              , std::string )>     welcome({"welcome"});
                 
dy::type<void ( std::string )>     quit({"quit"});


// определение делегатов исполнения:

void on_welcome (std::string a, std::string b) 
{
   std::cout << "on_welcome "<< a << " " << b << std::endl;
}
void on_quit (std::string a)
{
  std::cout << "on_quit "<< a << std::endl;
}

int main ()
{

   dy::map _map;
    _map[welcome] = on_welcome;
    _map[quit] = on_quit;
//    _map[quit] = on_welcome; // error
    
    _map[quit]("aaa");
    _map[welcome]("bb","cc");
//    _map[welcome]("dd"); // error
    
}




--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 20:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



но нам также нужно обращаться к карте по id... В этом случае мы можем возвращать только общий интерфейс исполнителя
(i_invoke), но для исполнения мигранта, нам будет достаточно..
тогда добавим в карту :
Код

struct map 
{
   ...
   i_invoker* operator [](const std::string& id)
   {
     items::iterator it = _items.find(id);
     if (it ==_items.end()) return NULL;
     else  return it->second.get();     
   }      
   ...
};



--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 21:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



сделаем пока условный "локальный" канал, который будет посылать мигранта сразу на выполнение
Код

struct channel 
{
   channel& operator << (const migrant& m)
   {
      if (i_invoker * inv = _map[m._id])
      {
         std::cout << m._id << " invoked";
//         (*inv)(m);
         std::cout << std::endl;
      }
      else std::cout << m._id <<" not found" << std::endl;
      
      return *this;
   }
   map _map;
};

тогда пользование условно будет:
Код

   dy::channel _ch;
    _ch._map[welcome] = on_welcome;
    _ch._map[quit] = on_quit;
    
    _ch << dy::migrant ({welcome.id, ""});
    _ch << dy::migrant ({"quit", ""});

http://liveworkspace.org/code/c2d8ebdb654c...7751350124981c2




--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 22:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



попробуем отделить карту от канала.. 
для этого напишем адаптер инвокера для карты :
Код

struct mapper : i_invoker
{
   void operator () (migrant const& m)
   {
      if (i_invoker * inv = _map[m._id])
      {
         std::cout << m._id << " invoked";
//         (*inv)(m);
         std::cout << std::endl;
      } 
      else std::cout << m._id <<" not found" << std::endl; 
   }
   
   mapper (map const& m) : _map(m)
   {
   }
   map const& _map;
};

также определим  в канале вектор инвокеров, установку и вызов  для них:
Код


struct channel 
{
   channel& operator << (const migrant& m)
   {
      responce (m);
      
      return *this;
   }
   void responce (const migrant& m)
   {
      for (respondents::iterator it = _respondents.begin(); 
           it != _respondents.end(); ++it)
     {      
       (**it)(m);
     }      
   }
   channel& operator +=(const map& m)
   {
      _respondents.push_back( std::shared_ptr<i_invoker>(new mapper(m)) );
      return *this;
   }
   typedef  std::vector<std::shared_ptr<i_invoker> > respondents;
   respondents _respondents;
};



теперь код тестовый код выглядит так :
Код

   dy::channel _ch;    
    dy::map _map, _map1;
    
    _ch += _map;
    _ch += _map1;
 
    _map[welcome] = on_welcome;
    _map[quit] = on_quit;
    
    _map1[quit] = on_quit;
  
    _ch << dy::migrant ({welcome.id, ""});
    _ch << dy::migrant ({"quit", ""});
    _ch << dy::migrant ({"wquit", ""});


http://liveworkspace.org/code/49e6f85dff00...7576ee504f10809

Это сообщение отредактировал(а) mes - 27.2.2011, 22:41


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 23:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



теперь попробуем облагородить вызов .. 

перегрузим тип для каждой сигнатуры, добавив ему оператор(), выполняющий роль конструктора :
Код


template<typename F>
struct type;

template<typename A1>
struct type<void(A1)>
{
   
   migrant operator ()(A1) const
   {
      return migrant ({id,""});
   }
   
   std::string id;
   
};

template<typename A1, typename A2>
struct type<void(A1,A2)>
{
   
   migrant operator ()(A1,A2) const
   {
      return migrant ({id,""});
   }
   
   std::string id;
   
};

тогда вызов будет выглядеть так :
Код

    _ch << welcome("","");    
    _ch << quit ("");

http://liveworkspace.org/code/cc96889ac487...3a8103d5d5fb7a4

Это сообщение отредактировал(а) mes - 28.2.2011, 01:24


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 23:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(mes @  27.2.2011,  22:04 Найти цитируемый пост)
перегрузим тип для каждой сигнатуры,

сделаем ответное для инвокера :

Код

template<typename A1>
struct invoker<void(A1)> : i_invoker 
{
   virtual void operator () (migrant const& m) 
   {
      A1 a1;    
      _function (a1);
   }
   std::function<void(A1)> _function;
};

template<typename A1, typename A2>
struct invoker<void(A1,A2)> : i_invoker 
{
   virtual void operator () (migrant const& m) 
   {
      A1 a1; A2 a2;    
      _function (a1,a2);      
   }
   std::function<void(A1,A2)> _function;
};

добавим сериалицию и в итоге  работающий каркас готов: 
http://liveworkspace.org/code/07f972915fb8...e174b5bb3e4a6b9

осталось облагораживание и исправление ошибок..

Это сообщение отредактировал(а) mes - 28.2.2011, 02:43


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.2.2011, 23:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



избавился от маппера в пользу boost::signal, заодно добавил разветление для каналов:
http://liveworkspace.org/code/39b7b6fd4079...7eeed50555a3614

добавил возможность выбора для политики инвокинга (signal, function) :
http://liveworkspace.org/code/6a215a11ed05...ef1d636c03b2d1d

также добавлен выбор политики и для канала :
http://liveworkspace.org/code/184cb6d49875...3538549dfa54124


Это сообщение отредактировал(а) mes - 28.2.2011, 12:03


--------------------
PM MAIL WWW   Вверх
mes
Дата 28.2.2011, 12:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



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

struct stc {

dy::type<void ( std::string
              , std::string )>     welcome;
                 
dy::type<void ( std::string )>     quit;

    stc () : welcome ({"welcome"})
           , quit({"quit"})
           {}

} _stc;

тогда тестовый пример выглядит так :
Код

    _map[_stc.welcome] = on_welcome;   
    _map[_stc.quit] = on_quit;
 
    _ch << _stc.welcome("hello"," world");    
    _ch << _stc.quit ("bye");

http://liveworkspace.org/code/8ded5287fb75...fe0ec911ca91999


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.3.2011, 16:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



немного изменив подход (подробности размышлений приведены в соседней теме) продолжаем.. 

легенда :
:: dy     - общие элементы обеспечения взаимодействия
:: dyco - описание конкретного протокола
::         - клиентская приложение

dy::proto<> - класс описывающий группу вызовов
dy::proto<>::call<Args...> - вызов группирующий из аргументов пакет сообщения
dy::free_fn<> - дин. интерфейс передачи сообщения как к свободной функции
dy::member_fn<> - дин. интерфейс передачи сообщения, как члену классу
dy::method<> - реализация вышеназванных интерфейсов
dy::map<>- карта соответствий вызова к методу
dy::channel - класс реализующий транспорт сообщения

dyco::msg - условное сообщение
dyco::msg_traits - свойства условного сообщения, необходимые для взаимодействия с dy

client, service - условные  обработчики сообщений 

пример :
http://liveworkspace.org/code/2bcfd7f5fc99...883f65a343e569d

//---------------------

:: dy

описание функторов формирующих пакеты сообщения, и их группы (протокола):
Код

//-->
template< typename Traits >
struct proto {
 
 typedef proto<Traits> proto_t; 
 
 static size_t next_index ()
 {
   if (counter_cur++ >= counter_end) throw -1;
   return counter_cur;
 }
 static size_t counter_cur;
 static size_t counter_end;
 
 proto ( size_t start  =0
       , size_t finish =0 )
 {
   counter_cur = start;
   counter_end = finish;
 } 

 template< typename...Args >
 struct call
 {
   call(typename Traits::id_type const& id) : id(id) 
   {}
   call() : id(Traits::make_id(next_index())) 
   {} 
   
   typename Traits::msg_type 
   operator () (Args... args) const
   {
       return Traits::template make_msg (id, args...);
   }
   typename Traits::id_type id;
 }; 
};

template< typename Traits >
size_t proto<Traits>::counter_cur;

template< typename Traits >
size_t proto<Traits>::counter_end;

//<--

динамические интерфейсы метода исполнения сообщения
Код


//-->
template < typename Msg >
struct free_fn
{
   virtual void operator () (Msg const&) =0;
};

template< typename That
        , typename Msg >
struct member_fn
{
   virtual void operator () (That&, Msg const& ) =0; 
};
//<--

реализации метода исполнения сообщения
Код

//-->
template < typename Traits
         , typename F
         , typename...Args >
struct method;

template < typename Traits
         , typename...Args >
struct method< Traits
             , free_fn< typename Traits::msg_type >
             , Args...
             > 
     : free_fn< typename Traits::msg_type >
{
   void operator () (typename Traits::msg_type const& msg)
   {  
      invoke<sizeof...(Args)>::
       apply( _function
            , Traits::template get_args<Args...>(msg) );
   }   
   typedef std::function<void(Args...)> function_t;
   function_t _function;
}; 

template < typename Traits
         , typename That
         , typename...Args >
struct method< Traits
             , member_fn< That, typename Traits::msg_type >
             , Args...
             > 
     : member_fn< That, typename Traits::msg_type >
{
   void operator () (That& that, typename Traits::msg_type const& msg)
   {       
      invoke<sizeof...(Args)>::
       apply( _function
            , Traits::template get_args<Args...>(msg)
            , that );
   }
   typedef std::function<void(That&, Args...)> function_t;
   function_t _function;
};
//<--


карта соответствий типа сообщения к вызову
Код

//-->
template <typename Traits, typename Fn>
struct map 
{   
   template<typename...Args>
   map 
   operator() ( typename proto<Traits>::template call<Args...> const& t
              , typename method<Traits, Fn, Args...>::function_t const& fn  ) 
   {      
     operator[](t) = fn;
     return *this;
   }
      
   template<typename...Args>
   typename method<Traits, Fn, Args...>::function_t&
   operator[] (typename proto<Traits>::template call<Args...> const& t) 
   {      
     Fn * mtd;
     typename items::iterator it = _items.find(t.id);
     if (it ==_items.end())
     {
          mtd = new method<Traits, Fn, Args...>;
         _items[t.id] = mtd;
     }
     else  mtd = it->second;
     
     return ( static_cast< method<Traits, Fn, Args...>* >(mtd)->_function);
   }
   
   Fn* operator [](const typename Traits::id_type& id) const
   {
     typename items::const_iterator it = _items.find(id);
     if (it ==_items.end()) return NULL;
     else  return it->second;  
   }     

   typedef std::map< std::string, Fn* > items;   
   items _items;   
};
//-->


Это сообщение отредактировал(а) mes - 27.3.2011, 19:38


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.3.2011, 17:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



::dyco

описание правил формирования сообщения :
Код

//-->
struct msg
{
   std::string command;
   std::vector< std::string > args;
};
//<--
//-->
struct make_args
{
   template < typename V
            , typename A
            , typename...Args >
   static void 
   apply (V & v , A a , Args...args)
   {
      std::ostringstream os;
      os << a;
      v.push_back (os.str());
      
      apply (v, args...);
   }     
   template < typename V>
   static void 
   apply (V & v) {} 
};

template<size_t N>
struct take_args
{
   template < typename V
            , typename Seq >
   static void 
   apply (Seq & seq ,V const& v)
   {
      std::istringstream is( v[N-1] );
      
      auto & a = std::get<N-1>(seq);
    
      is >> a;

      take_args<N-1>::apply (seq, v);
   }     
};
template<>
struct take_args<0>
{
   template < typename V
            , typename Seq >
   static void 
   apply (Seq & seq ,V const& v)
   { }
};

struct msg_traits
{
   typedef std::string id_type;
   typedef msg         msg_type;

   static id_type 
   get_id (msg_type const m) {
   
      return m.command;
   }
   
   static id_type
   make_id (size_t index) {
       
       std::ostringstream os;
       os <<"id_"<< index;
   
      return os.str();      
   }
       
   template <typename...Args>
   static std::tuple<Args...> 
   get_args (msg_type const& msg) {
      std::tuple<Args...>  seq;
      take_args<sizeof...(Args)>::apply (seq, msg.args);
      
      return seq;
   }
    
   template <typename...Args>
   static msg_type
   make_msg (const id_type& id
            ,const Args&... args) {
            
      msg_type m({ id });     
      make_args::apply (m.args, args...);

      return m;
   }
};

//<--

условные типы вызовов протокола 
Код

//-->
struct cts : dy::proto<msg_traits> {
   cts () : proto_t (0,10) {} 
   
   call<std::string, std::string> user; // login
   call<std::string> quit;    // logout    
};

struct stc : dy::proto<msg_traits> {
   stc () : proto_t (10,20) {} 
   
   call<std::string> ident; // login ok
   call<std::string> quit; // logout ok
         
};


Это сообщение отредактировал(а) mes - 27.3.2011, 19:36


--------------------
PM MAIL WWW   Вверх
mes
Дата 27.3.2011, 17:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



пример сервиса
Код

struct service
{
//--     
   void on_user (const std::string& name, const std::string& pass) {
      std::cout << "service::on_user(" << name << "," << pass <<"); "<< std::endl;      

      if (true)
        _responce ( _stc.ident(name) );
      else  
        _responce ( _stc.quit("error") );     
   }
   void on_quit  (const std::string& s) {
      std::cout << "service::on_quit(" << s << "); " << std::endl; 
     
      _responce ( _stc.quit("bye bye") );
   }
   
   void operator () (const dyco::msg& m) {
          
      dy::dispatch (_map, this, m) ;      
   }
//--   
   dyco::responce_fn_t _responce;
//--   
   typedef dy::map<dyco::msg_traits, dy::member_fn<service, dyco::msg> > map_t;
   static map_t _map;   
 
};

пример пользователя
Код


struct client
{
//--     
   void on_login_btn ()
   {
     std::cout << "client::on_login_btn(); "<< std::endl;         

    _responce ( _cts.user("name", "pass") );
   }
   void on_logout_btn ()
   {
     std::cout << "client::on_logout_btn(); "<< std::endl;         

    _responce ( _cts.quit("bye") );
   }   
//--    
   void on_ident (const std::string& name) {
     std::cout << "client::on_ident(" << name <<"); "<< std::endl; 
   }
   void on_quit  (const std::string& s) {
     std::cout << "client::on_quit(" << s << "); " << std::endl; 
   }
   
   void operator () (const dyco::msg& m) {
   
      dy::dispatch (_map, this, m) ;      
   }
//--
   dyco::responce_fn_t _responce;
//--     
   typedef dy::map<dyco::msg_traits, dy::member_fn<client, dyco::msg> > map_t;
   static map_t _map; 
};
//<--


инициализация условных "таблиц виртуальных функций"
Код

//-->
service::map_t service::_map = service::map_t()
(_cts.user,  &service::on_user)
(_cts.quit,  &service::on_quit);

client::map_t client::_map = client::map_t()
(_stc.ident, &client::on_ident)
(_stc.quit,  &client::on_quit);
//<--

пример взаимодействия :
Код

int main ()
{
    client _client;    
    service _service;
    
    _client._responce = std::ref(_service);  
    _service._responce = std::ref(_client);

     std::cout << "-------"<< std::endl;         

    _client.on_login_btn ();
     std::cout << "-------"<< std::endl;    

    _client.on_logout_btn ();
     std::cout << "-------"<< std::endl;        
   
     std::cout << "ok"<< std::endl;
}


Это сообщение отредактировал(а) mes - 27.3.2011, 19:34


--------------------
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1112 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.