Перейти к содержанию
    

Система, управляемая событиями и SST(super-simple tasker)

Поступило предложение тему разделить и основное обсуждение озаглавить "Система, управляемая событиями и SST(super-simple tasker)".

И перенести, наверное, в "Операционные системы".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

NodeJS свободно может работать без ОС и потоков вообще (и работает - есть даже реализации под Embedded ссылки я приводил), в то время, как классический многопоточный код на C работать без ОС не сможет - ему нужны треды, мютексы и семафоры.

Точно так же и SST - может работать как поверх любой ОС, так и сам на голом МК, хоть на AVR. Он не привязан к железу и свободно реализовывается в 200-300 строк на С++. Да, у меня часть написана на ассемблере(строчек на 20) но это было сделано только ради небольшого увеличения производительности на конкретном камне(Кортекс).

 

Ессно в конкретном тесте Node - это процесс по верх ОС, но это вынужденная мера, тк весь мир работает на линуксах и виндах, а не на голом железе.

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

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

 

99% задач, которые делают на FreeRTOS и подобных - решаются на SST более простым, натуральным способом и требуют в разы меньше памяти.

 

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

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

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

У меня в моем SST данные идут порциями, натуральными для конкретного устройства. Например, если это флешка - то страницами. При чем это все происходит через высокоуровневые структуры и пользователь не видит что там физически происходит.

 

Вот пример из моего же кода. Открытие, чтение файла и передача его в сокет в однопоточном асинхронном Node

    var readable = fs.createReadStream('file.txt');
    readable.pipe(socket);

При чем если написать принт после этих строчек, то принт сработает до окончания передачи файла, а не после (если файл достаточно большого размера или канал медленный.

 

А вот в многопоточном синхронном С:

FILE* fd = fopen("file.txt","r");
    if(fd==0){
        perror("fopen");
        goto exit1;
    }

    // read and send file - blocking!
    char buffer[4096];
    while(1){
        // read chunk
        size_t r = fread(buffer, 1, sizeof(buffer), fd);
        if(r<=0)break;
        // send
        r = send(sock, buffer, r, 0);
        if(r<=0){
            perror("Thread_connection: socket error");
            break;
        }
    };
    fclose(fd);

Где проще?

В моем SST все примерно так и выглядит - передача через высокоуровневые обьекты(очереди/пайпы называйте как хотите) и все прозрачно для пользователя, и очень быстро - оверхеда минимум, меньше уже практически невозможно.

 

Уже не один человек высказал мнение, что SST это не так круто как вам кажется и имеет кучу ограничений и проблем. Однако вы по прежнему упрямствуете и, как вы сами выразились, вдуваете себе в голову On wink.gif

Покажите чем реально SST хуже потоков? Какую задачу на SST реализовать сложнее, чем на потоках? Давайте мне любой многопоточный код и я его переведу в более красивый и быстрый асинхронный SST-шный ;)

Мы говорим о реальных практических задачах, а не где то там что-то там требует тайм-кванты. Давайте реальную задачу с тайм-квантами и я покажу как от них избавится :)

 

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

 

Вот пример аналогичного кода(чтение из терминала через Uart и передача другому чипу) в моем SST:

void Term::UartReader::operator()(
        T_RxFifo_Base* fifo, const uint8_t* ptr, int len)
{
    term->telit->cmux.send(TelitMux_Gsm, ptr, len, [fifo](){
        fifo->signal_dataAccepted();
    });
}

По-больше кода чем у Node 3 строчки против одной, но это C++, такой он есть verbose язык.. Хотя могу и однй сделать, запросто, если будет такая необходимость.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2brag

 

В приведенном вами коде проскакивает

delegate<void()>

Может быть выложите реализацию делегатов?

Сам использую свой велосипед и собираю велосипеды других с целью повышения образования.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поступило предложение тему разделить и основное обсуждение озаглавить "Система, управляемая событиями и SST(super-simple tasker)". Будут возражения? Или переименуем всю тему? Автор, что думаете?

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

Хотя интересно было узнать то, что помимо стандартных new/delete, существуют и свои средства (стратегии) для управления кучами.

В институте был курс "Организация вычислительных процессов в ЭВМ", в частности с понятиями об операционных системах и их работе с очередями пользовательских программ, были и книги по этой теме. А сейчас какие есть книги (ru/eng) на эту же тематику, но c учетом новых подходов?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хотя интересно было узнать то, что помимо стандартных new/delete, существуют и свои средства (стратегии) для управления кучами.

dew/delete никуда не деваются, но мы их можем перегружать и иметь свою реализацию. Потом есть всякие smart-pointerы и reference count-ы - это для того, чтобы голова за delete не болела. Но их надо использовать с осторожностью(равно как и delete)

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

 

Может быть выложите реализацию делегатов?

Сам использую свой велосипед и собираю велосипеды других с целью повышения образования.

Выложу ниже. Велосипеды/не велосипеды, но эта реализация отлично работает многие годы. А сделал я ее потому что когда я попытался применить нормальный std::function на Atmega8, то это сразу сожрало всю оператирку(и все рано ее не хватило), потянуло за собой кучу ненужного мусора. В итоге сделал этот велосипед na 80 строк(вместо нескольких тысяч в std) за час.

 

Держите статическую версию, в большинстве случаев ее достаточно. А на слабых МК (до 8-16кб оперативки) только она и уместна

template<class T, int d_size=1> class delegate;

template<class TRet, class... TArgs, int d_size>
class delegate<TRet(TArgs...),d_size> {
public:
delegate(){}

 // copy constructor and copy operator=
template<int size_another>
delegate(const delegate<TRet(TArgs...),size_another>& another){
	operator=(another);
}

template<int size_another>
delegate& operator=(const delegate<TRet(TArgs...),size_another>& another){
	static_assert(sizeof(another) <= sizeof(*this), "RHS too large");
	wrapper_ = another._private_get_wrapper();
	for(unsigned i=0; i<sizeof(another._private_get_data()) / sizeof(data_.vp[0]); i++){
		data_.vp[i] = another._private_get_data().vp[i];
	}
	return *this;
}

 // lambda, static function and function object
template<class TLambda> 
delegate(const TLambda& lambda){
	setFunction(lambda);
}

template<class TLambda> 
delegate& operator=(const TLambda& lambda){
	return setFunction(lambda);
}

 // For lambda, static function and function object
template<class TLambda>
delegate& setFunction(const TLambda& lambda){
	struct Wrapper{
		Wrapper(const TLambda& lambda) : la(lambda) {}

		static TRet wrapper(void* data, TArgs ... args){
			Wrapper *w = static_cast<Wrapper*>(data);
			return w->la(args...);
		}

		void* operator new(size_t, void *place){ return place; }

		TLambda la;
	};
	static_assert(sizeof(Wrapper) <= sizeof(data_), "Wrapper too large");

	new(&data_) Wrapper(lambda);
	wrapper_ = &Wrapper::wrapper;
	return *this;
}

 // invoke wrapper
  TRet operator()(TArgs ... args){
	return wrapper_(&data_, args...);
}

TRet operator()(TArgs ... args)const{
	return wrapper_(const_cast<t_data*>(&data_), args...);
}

private:
typedef TRet (*t_wrapper)(void*, TArgs...);

t_wrapper wrapper_;
union t_data{
	void *vp[d_size];
} data_;

// really private
public:
const t_wrapper& _private_get_wrapper()const{ return wrapper_; }
const t_data& _private_get_data()const{ return data_; }
};

 

Несмотря на такое изобилие кода(который делается 1 раз) на самом деле для выполнения делегата компилятор генерирует 2 инструкции(на кортексе) - чтение указателя wrapper и прыжок на него :)

А тело самой функции-делегата встраивает в wrapper, фактического вызова функции внутри wrappera нет.

Создание делегата тоже очень простая операция - запись указателя(одна инструкция) и запись данных(обычно тоже одна инструкция, в зависимости от обьема данных - сколько слов, столько и инструкций или одна STM для всех)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

dew/delete никуда не деваются, но мы их можем перегружать и иметь свою реализацию. Потом есть всякие smart-pointerы и reference count-ы - это для того, чтобы голова за delete не болела. Но их надо использовать с осторожностью(равно как и delete)

В моем случае под 7 версией FreeBCD на индустриальном компьютере в режиме реального времени одновременно работают пять равно приоритетных программ. Каждая через new/delete (без перегрузок) создает для себя динамические объекты в куче. Если куча общая, то ОС должна четко организовывать управление кучей для всех программ. Хотел узнать (прочесть) об этом немного больше, хотя и так кое-что уже прояснилось.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Очень бы хотелось увидеть как бы вы обслуживали много клиентов, рисовали GUI и делали еще то что нужно и именно на голой системе и именно со своим SST и чтоб в масштабах ВСЕЙ системы был бы один стек и один поток. Собственно это то, с чего мы начинали нашу дискуссию.

 

NodeJS свободно мо жет работать без ОС и потоков вообще (и работает - есть даже реализации под Embedded ссылки я приводил),

Кто асинхронный ввод/вывод то будет обслуживать?

Всё это твёрдо стоит на крепких ногах операционной системы

http://man7.org/linux/man-pages/man7/aio.7.html

https://msdn.microsoft.com/ru-ru/library/wi...3(v=vs.85).aspx

И для того чтобы организовать то-же самое вам придется реализовать примерно такие-же механизмы в своей системе.

И вот тут уже будет существенная разница между SST + один стек на всё провсё и нормальным подходом!

Хватит уже нам тут рекламировать как хорошо и естественно всё проходит без блокировок, проснитесь и откройте глаза )

 

Вот пример из моего же кода. Открытие, чтение файла и передача его в сокет в однопоточном асинхронном Node

Показать как мало кода нужно на верхнем уровне, когда всё завёрнуто красиво мы все можем. Это уже больше похоже на какое-то поклонение высокоуровневому коду )

 

Начали мы с SST где один стек и один поток на всех в системе давайте и будем продолжать в том-же духе. А не гордиться тем как здорово можно с помощью ядра ОС поставить в очередь IO операцию и подписаться на события (говоря языком JS) от этой операции.

Правильно уже вам тут говорили, вы даже реализацию TCP/IP со своим SST запаритесь делать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Очень бы хотелось увидеть как бы вы обслуживали много клиентов, рисовали GUI и делали еще то что нужно и именно на голой системе и именно со своим SST и чтоб в масштабах ВСЕЙ системы был бы один стек и один поток. Собственно это то, с чего мы начинали нашу дискуссию.

Так я именно это и постоянно делаю! У меня GUI, сложные вычисления, куча IO(флешки,ацп,датчики 1wire) отлично работают на SST без единых тормозов и проблем. На многопоточной оси аля FreeRTOS постоянно были проблемы, то гонки, то оверхед большой, то еще что-то.

И для того чтобы организовать то-же самое вам придется реализовать примерно такие-же механизмы в своей системе.

Ну так они реализованы - это SST :) При чем довольно просто, сам SST - это около 300 строк кода, динамическая очередь - почти 200 строк кода, очередь задач - 60 строк кода, делегат(Вы уже его видели выше) - 80 строк кода

 

Правильно уже вам тут говорили, вы даже реализацию TCP/IP со своим SST запаритесь делать.

Сделана и отлично работает.

 

Начали мы с SST где один стек и один поток на всех в системе давайте и будем продолжать в том-же духе.

Ну так я показал, как на своем SST я работаю с DataFlash - привел полный код операции ее стирания. Что там непонятного? Могу любую другую задачу показать как я делаю ее на SST. Говорите какую - приведу код, тк у меня практически все задачи уже решены в виде простейших библиотек и я их использую когда надо - строю программу из этих кубиков.

 

В моем случае под 7 версией FreeBCD на индустриальном компьютере в режиме реального времени одновременно работают пять равно приоритетных программ. Каждая через new/delete (без перегрузок) создает для себя динамические объекты в куче. Если куча общая, то ОС должна четко организовывать управление кучей для всех программ. Хотел узнать (прочесть) об этом немного больше, хотя и так кое-что уже прояснилось.

Ну в таких сложных ОС, как FreeBSD управление кучей очень сложное. Куча для каждого процесса своя, а для потоков внутри этого процесса общая, и ессно ядру нужно ею управлять так, чтобы это не сказывалось на других процессах, там все довольно сложно и вникать туда смысла нет - жизни не хватит.

Зато есть смысл делать динамику на embedded, где и памяти мало, и других ресурсов, но чувствовать себя там практически так, как будто под PC пишешь.

 

В FreeBSD там все довольно грамотно да и работать в асинхронном стиле(без ожиданий и циклов) там гораздо проще, чем в линуксе. В линуксе до сих пор нет нормальных асинхронных IO, есть костыли вроде epoll. Тогда как в FreeBSD есть Kqueue. Думаю, если мой тест запустить на FreeBSD, то NodeJS еще сильнее убьет в хлам классические pthread.

 

А тем временем провел другой тест - сервер выдает не файл(который ОС выдает из кеша, а не читает по факту с диска) а рандомные данные по таймеру по 100 байт каждые 10 милисекунд вечно, пока клиент не отключится.

Вот так это выглядит на NodeJS, в однопоточном асинхронном стиле:

        var timer;
        function on_socket_closed(){
            n_connections--;
            clearInterval(timer);
        }

        // Periodically send random data to client
        timer = setInterval(function(){
            var str;
            for(var i=0; i<100; i++){
                str+= String.fromCharCode(Math.random()*256);
            }
            socket.write(str);
        }, 10);

То есть для каждого клиента мы создаем таймер, в обработчике события которого генерируется рандом и потом ставится в очередь на отправку клиенту. Не шибко эффективный способ,тк время генерации рандома велико - тк это мы делаем синхронным блокирующим способом - в цикле for. правильно было бы прoчитать эти данные из готового асинхронного рандом-генератора (типа /dev/urandom) и передать клиенту - моментально, одной строчкой. Но тк я буду делать то же самое на С, только в многопоточном стиле - специально сделал так, чтобы сразу не убить в хлам многопоточную модель :)

И того такой сервер на моем ноуте может обслуживать 3900 клиентов одновременно и занимает при этом 80МБ оперативки. При чем клиент запущен на том же ноуте, асинхронный тот же, что и прежний, на JS.

 

Сейчас наклепаю на C в мнгопоточном блокирующем стиле.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Всё всё, хватит, мы уже поняли что на Node.js можно в две строки запустить сервер и он будет быстро обрабатывать большое кол-во клиентов.

Покажите уже как всё это реализуется когда во всей системе один поток и один стек для всех ) Собсвтенно выше я уже написал всё тоже самое)

 

Ваш пример не работает в один поток потому что ядро работает параллельно с вами, в классическом потоке, со своим стеком и шлет вам в юзерспэйс приветы(ивэнты) с глубины ring0 :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так я уже показал, как я работаю с Датафлеш. Привел полный код операции стирания http://electronix.ru/forum/index.php?showt...t&p=1447897

Хотите на код самой SPI посмотреть? Запросто, сейчас выложу

Вот

#include <drv/LPC17xx/ssp.hpp>
#include <kernel-sst/taskqueue.hpp>

class SspTaskQueue : public TaskQueue<48>{
public:
SspTaskQueue(Ssp* ssp, SST::TPriority priority):
	TaskQueue(priority),
	ssp(ssp) {}

// proxy, use inside tasks
int dmaChannelRx()const{ return ssp->dmaChannelRx(); }
int dmaChannelTx()const{ return ssp->dmaChannelTx(); }

bool DmaWriteRead(const void *src, void *dst, int len){
	return ssp->DmaWriteRead(src,dst,len);
}

void setup(uint8_t size, bool cpol, bool cpha, uint16_t clkdiv){
	return ssp->setup(size, cpol, cpha, clkdiv);
}

void flush()const{
	return ssp->flush();
}

private:
Ssp *ssp;
};

Или мало?

Тогда вот

#include "dma.hpp"

class Ssp{
public:
Ssp(int port);
 // DMA
int dmaChannelRx()const{ return dma_channel_rx; }
int dmaChannelTx()const{ return dma_channel_tx; }

bool DmaWriteRead(const void *src, void *dst, int len){
	if(Dma::isChannelsBusy(dma_channel_rx,dma_channel_tx))return false;
	start_dma_read(dst,len);
	start_dma_write(src,len);
	return true;
}

// Дальше можно не смотреть, это чисто платформо-зависимый код
private:
void start_dma_write(const void *src, int len){
	Dma::startChannel(dma_channel_tx,
		src, (void*)&ssp->DR, 0, len,
		DMACCControl_xBSize_1, DMACCControl_xBSize_1,
		DMACCControl_DWidth_8, DMACCControl_DWidth_8,
		true, false,
		static_cast<DMA_Peripheral>(0), static_cast<DMA_Peripheral>(dma_peripheral_tx),
		DMA_TransferType_Mem2Periph
	);
}

void start_dma_read(void *dst, int len){
	Dma::startChannel(dma_channel_rx,
		(void*)&ssp->DR, dst, 0, len,
		DMACCControl_xBSize_1, DMACCControl_xBSize_1,
		DMACCControl_DWidth_8, DMACCControl_DWidth_8,
		false, true,
		static_cast<DMA_Peripheral>(dma_peripheral_rx), static_cast<DMA_Peripheral>(0),
		DMA_TransferType_Periph2Mem
	);
}

private:
LPC_SSP_TypeDef *ssp;
int8_t dma_channel_rx;
int8_t dma_channel_tx;
uint8_t dma_peripheral_rx;
uint8_t dma_peripheral_tx;
uint8_t dummy_ff;

Хватит? Или еще dma кинуть? Там кроме платформозависимого больше ничего нет.

 

Ваш пример не работает в один поток потому что ядро работает параллельно с вами, в классическом потоке, со своим стеком и шлет вам в юзерспэйс приветы(ивэнты) с глубины ring0 sm.gif

Кто мешает работать без этого ядра, потоков и ring0, на простом МК вроде Атмега8 и получить точно такое же поведение программы, как в NodeJS ? Ведь для программы потоки не нужны, они нужны(а нужны ли?) конкретной оси(линуксу) для планировки программы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Всё всё, хватит, мы уже поняли что на Node.js можно в две строки запустить сервер и он будет быстро обрабатывать большое кол-во клиентов.

Покажите уже как всё это реализуется когда во всей системе один поток и один стек для всех ) Собсвтенно выше я уже написал всё тоже самое)

 

Ваш пример не работает в один поток потому что ядро работает параллельно с вами, в классическом потоке и шлет вам вюзерспэйс приветы(ивэнты) с глубины ring0 :)

Вероятно, есть критерии, когда SST уместна, а когда лучше применять многопоточный вариант.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вероятно, есть критерии, когда STT уместна, а когда лучше применять многопоточный вариант.

Естественно есть. Например пользовательские ОС, где пользователь может запускать свои или чужие приложения. И то, в нормальных ОС, типа FreeBSD для каждого процесса есть по сути свой SST - это kqueue. То есть приложения планируются в многопоточном стиле, а работу внутри приложений можно делать в однопоточном асинхронном, с приоритетами - вобщем полный SST. В линуксе (по крайней мере на сколько мне известно) есть костыль в виде epoll, через который и реализовано все в таких штуках, как Nginx или NodeJS.

NodeJS - это не SST, у него один приоритет, а у SST их много. Если в NodeJS где либо втулить тяжелый или вечный цикл - вся программа умрет, а если это сделать в SST - то будет продолжать работать, умрут только менее приоритетные задачи. SST - это продолжение NodeJS.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Естественно есть. Например пользовательские ОС, где пользователь может запускать свои или чужие приложения.

Нет ли у Вас ссылок на какую-нибудь литературу по SST (необязательно для embedded)?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Так что это миф, когда говорят, что в многопоточной ОС криво написанное приложение не сможет повлиять на другие - очень даже может, и не просто повлиять, а завалить всю ОС намертво. Еще один плюс в пользу асинхронщины :)

Сколько памяти заняло - не знаю, тк система умерла, пришлось жать reset, но думаю, что всю.

Что успел заметить - сервак успел обработать 1160 коннектов(это 1162 потока - 1160 клиентских + вотчер + главный поток, принимающий коннекты)

char buffer[4096];
    int i,r;
    while(1){
        for(i=0; i<100; i++)buffer[i] = rand();
        r = send(sock, buffer, 100, 0);
        if(r<=0){
            perror("Thread_connection: socket error");
            break;
        }
        usleep(10000);
    };

Так что однопоточные приложения круче и проще многопоточных, даже на многопоточных осях, не говоря уже про чисто асинхронные, типа SST :)

 

Нет ли у Вас ссылок на какую-нибудь литературу по SST (необязательно для embedded)?

Да там то и ссылок особо быть не может. А то, что есть - в этом топике приводили, в том числе и на русском.

Тут нужно научиться мыслить асинхронно, без блокировок - перестроить свой мозг. Привыкнуть применять всякие замыкания, колбеки. А затем SST само собой получится.

Для тренировки отлично подходит замечательный инструмент - NodeJS, язык яваскрипт - синтаксис у него сишный, для C-программиста проблем не будет.

 

Для работи на МК в асинхронном стиле вообще ничего левого не нужно - нужен голый C++ и умение программировать. А тот, кто будет дергать ивенты там уже есть - это контроллер прерываний. Какие ще на МК могут быть ивенты, кроме прерываний?

 

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

Если вы видите, что у вас длинный цикл(больше, чем скажем 8 шагов для МК и около 100 для PC) - значит уже делаете что-то не так, особенно, если это операции ввода/вывода (тот же printf). В асинхронщине рулит рекурсия, а это уже шаги в сторону функционального программирования.

Речь идет не о сложных математических задачах, а о программировании в целом. Конечно, если умножение матриц или какой-нибудь криптографический алгоритм требует 100 000 циклов, то трогать их не нужно. Просто эту операцию нужно выполнять на другом уровне приоритета, чтобы не мешало остальным(ІО, GUI).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Почерпнул много интересного из этого обсуждения. Прямо как будто зашел на электроникс лет 8 назад.

 

brag, не могли бы вы прокомментировать этот кусочек кода:

template<class T, int d_size=1> class delegate;

template<class TRet, class... TArgs, int d_size>
class delegate<TRet(TArgs...),d_size> {
public:

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...