Добрый день. Решил попробовать тестирование, и сразу напоролся на вопрос, который не знаю, как решить.
Делаю тест в отдельном проекте.
Имею примерно следующие файлы/классы (показано сразу содержимое .h и .cpp файлов):
class Obj
{
public:
int Prop() const;
};
int Obj::Prop() const
{
returnчто-нибудь;
}
и
#include"Obj.h"class ObjProcessor
{
public:
int DoProcessing(const Obj& a);
};
int ObjProcessor::DoProcessing( const Obj& a )
{
if (a.Prop())
return -1;
return 0;
}
Как организовать тестирование класса ObjProcessor? Когда я подключаю его .h-ник в проект теста, он автоматически тянет за собой файлы класса Obj, при попытке переопределения последнего, само собой, компилятор ругается на это переопределение. Можно, конечно, передавать в ObjProcessor интерфейс, а не класс, но это бывает неприемлемо. В то же время искусственное управление состоянием реального Obj тоже может быть невозможным, если он с какой-нить мудреной логикой. Как тут действовать тогда, что-то я не соображу.
P.S. Может, конечно, сегодняшний марафон виноват, и завтра мозг вместе с ногами очухается, будет чувствовать себя приемлемо и выдаст решение; а ну как нет?
А почему интерфейс неприемлемо? Вообще-то, все так и делают..
Здравствуйте, DaStartuper, Вы писали:
DS>Добрый день. Решил попробовать тестирование, и сразу напоролся на вопрос, который не знаю, как решить.
DS>Делаю тест в отдельном проекте.
DS>Имею примерно следующие файлы/классы (показано сразу содержимое .h и .cpp файлов):
DS>
DS>#include"Obj.h"
DS>class ObjProcessor
DS>{
DS>public:
DS> int DoProcessing(const Obj& a);
DS>};
DS>int ObjProcessor::DoProcessing( const Obj& a )
DS>{
DS> if (a.Prop())
DS> return -1;
DS> return 0;
DS>}
DS>
DS>Как организовать тестирование класса ObjProcessor? Когда я подключаю его .h-ник в проект теста, он автоматически тянет за собой файлы класса Obj, при попытке переопределения последнего, само собой, компилятор ругается на это переопределение. Можно, конечно, передавать в ObjProcessor интерфейс, а не класс, но это бывает неприемлемо. В то же время искусственное управление состоянием реального Obj тоже может быть невозможным, если он с какой-нить мудреной логикой. Как тут действовать тогда, что-то я не соображу.
DS>P.S. Может, конечно, сегодняшний марафон виноват, и завтра мозг вместе с ногами очухается, будет чувствовать себя приемлемо и выдаст решение; а ну как нет?
Здравствуйте, ulu, Вы писали:
ulu>А почему интерфейс неприемлемо? Вообще-то, все так и делают..
Интерфейс — это виртуальные фукнции. Виртуальные функции — это лишняя память, плюс процессорное время на вычисления при вызовах.
Здравствуйте, DaStartuper, Вы писали:
DS>Здравствуйте, ulu, Вы писали:
ulu>>А почему интерфейс неприемлемо? Вообще-то, все так и делают.. DS>Интерфейс — это виртуальные фукнции. Виртуальные функции — это лишняя память, плюс процессорное время на вычисления при вызовах.
Это по указателю на каждый вирт. метод (для x86 — 4 байта) и 1(!!!) лишняя инструкция для вычисления адреса косвенного вызова на любом вменяемом компиляторе.
Неужели у вас эти объекты создаются в условиях жёсткой нагрузки сотнями тысяч и также зверски используются?
ИМХО накладные расходы на виртуальный вызов давно перестали быть проблемой.
Здравствуйте, Мишень-сан, Вы писали:
МС>Здравствуйте, DaStartuper, Вы писали:
DS>>Здравствуйте, ulu, Вы писали:
ulu>>>А почему интерфейс неприемлемо? Вообще-то, все так и делают.. DS>>Интерфейс — это виртуальные фукнции. Виртуальные функции — это лишняя память, плюс процессорное время на вычисления при вызовах.
МС>Это по указателю на каждый вирт. метод (для x86 — 4 байта) и 1(!!!) лишняя инструкция для вычисления адреса косвенного вызова на любом вменяемом компиляторе. МС>Неужели у вас эти объекты создаются в условиях жёсткой нагрузки сотнями тысяч и также зверски используются? МС>ИМХО накладные расходы на виртуальный вызов давно перестали быть проблемой.
Поправочка. В экземпляре у Вас вообще будет один лишний указатель на vtable.
Если ты пишешь драйвер или Самую Быструю Игру, то накладные расходы -- проблема, наверное. И лучше отказаться от объектов, и весь код писать в main, чтобы сэкономить на вызовах процедур. А если не проблема -- зачем вообще с C++ мучиться ?
Здравствуйте, Мишень-сан, Вы писали:
МС>Здравствуйте, DaStartuper, Вы писали:
DS>>Здравствуйте, ulu, Вы писали:
ulu>>>А почему интерфейс неприемлемо? Вообще-то, все так и делают.. DS>>Интерфейс — это виртуальные фукнции. Виртуальные функции — это лишняя память, плюс процессорное время на вычисления при вызовах.
МС>Это по указателю на каждый вирт. метод (для x86 — 4 байта) и 1(!!!) лишняя инструкция для вычисления адреса косвенного вызова на любом вменяемом компиляторе. МС>Неужели у вас эти объекты создаются в условиях жёсткой нагрузки сотнями тысяч и также зверски используются? МС>ИМХО накладные расходы на виртуальный вызов давно перестали быть проблемой.
Здравствуйте, Мишень-сан, Вы писали:
МС>Неужели у вас эти объекты создаются в условиях жёсткой нагрузки сотнями тысяч и также зверски используются?
Где-то, может, и единицами, а где-то сотнями тысяч (правда, редко, но все-таки). Скажем, это точки
данных телеметрии. За час полета этих точек набирается мама не горюй, когда вопрос ставится об обработке
аналоговых параметров.
Когда я подключаю его [ObjProcessor-а] .h-ник в проект теста, он автоматически тянет за собой файлы класса Obj, при попытке переопределения последнего, само собой, компилятор ругается на это переопределение.
Что-то я не понимаю, как тут может быть переопределение — проекты-то разные !
Здравствуйте, DaStartuper, Вы писали:
DS>Здравствуйте, Мишень-сан, Вы писали:
МС>>Неужели у вас эти объекты создаются в условиях жёсткой нагрузки сотнями тысяч и также зверски используются? DS>Где-то, может, и единицами, а где-то сотнями тысяч (правда, редко, но все-таки). Скажем, это точки DS>данных телеметрии. За час полета этих точек набирается мама не горюй, когда вопрос ставится об обработке DS>аналоговых параметров.
В любом случае, прежде, чем пойти по более сложному пути, чреватому бОльшим количеством багов (из-за проблем с тестированием), лучше провести небольшое исследование на тему того, нсколько в действительности все будет медленнее работать.
Здравствуйте, okman, Вы писали:
O>Что-то я не понимаю, как тут может быть переопределение — проекты-то разные !
Ну а когда я пишу в тесте #include "../ProjectBeingTested/ObjProcessor.h", а в этом
ObjProcessor.h уже есть директива #include "Obj.h", то этот Obj.h берется из той
же папки, где лежит ObjProcessor.h. Невзирая на то, что это папка — в другом проекте.
Рассчитывается же относительный путь для включаемого файла, а не для включающего.
Здравствуйте, ulu, Вы писали:
ulu>В любом случае, прежде, чем пойти по более сложному пути, чреватому бОльшим количеством багов (из-за проблем с тестированием), лучше провести небольшое исследование на тему того, нсколько в действительности все будет медленнее работать.
Ну да. Плохо только, что я уже давно пошел по этому пути, а щас решил присобачить тесты к имеющемуся коду, чтобы можно было что-то менять. Потому что библиотека достигла уже той стадии, на которой менять что-то просто так — очень страшно В этом и проблема. Конечно, можно, как и прежде, сделать как получится, а баги ловить потом по отзывам пользователей — но это же жестоко.
Здравствуйте, DaStartuper, Вы писали:
ulu>>А почему интерфейс неприемлемо? Вообще-то, все так и делают.. DS>Интерфейс — это виртуальные фукнции. Виртуальные функции — это лишняя память, плюс процессорное время на вычисления при вызовах.
Ты написал пост. Пост — это символы на диске. Запись на диск — это нагрев диска. Нагрев — это глобальное потепление. Глобальное потепление — это экологическая катастрофа. Экологическая катастрофа — это смерть всего живого. Ну-ка перестань, ты на этой планете не один!
Здравствуйте, DaStartuper, Вы писали:
DS>Ну да. Плохо только, что я уже давно пошел по этому пути, а щас решил присобачить тесты к имеющемуся коду, чтобы можно было что-то менять. Потому что библиотека достигла уже той стадии, на которой менять что-то просто так — очень страшно В этом и проблема. Конечно, можно, как и прежде, сделать как получится, а баги ловить потом по отзывам пользователей — но это же жестоко.
В этом случае хорошо бы покрыть систему тестами, как она есть, т.е., не рефакторить пока. Это будут интеграционные тесты, и, если один класс использует другой, мы тестируем их в связке.
Здравствуйте, DaStartuper, Вы писали:
DS>Как организовать тестирование класса ObjProcessor? Когда я подключаю его .h-ник в проект теста, он автоматически тянет за собой файлы класса Obj, при попытке переопределения последнего, само собой, компилятор ругается на это переопределение. Можно, конечно, передавать в ObjProcessor интерфейс, а не класс, но это бывает неприемлемо. В то же время искусственное управление состоянием реального Obj тоже может быть невозможным, если он с какой-нить мудреной логикой. Как тут действовать тогда, что-то я не соображу.
Можно переопределить, есть способы, разумеется надо переколбасить весь проект, что бы файлы были заменяемыми
Можно унаследовать, но для этого нужны виртуальные методы
Другой способ — ввести макрос, и там подсовывать _разные_ классы как параметр.
Самый толковый способ — это ввести интерфейс или добавить виртуальные методы, что бы можно было переопределить только их.
Но вообще твой вопрос достаочно странный — как обрезать депенденси без образания депенденси
DS>Здравствуйте, ulu, Вы писали:
DS>Интерфейс — это виртуальные фукнции. Виртуальные функции — это лишняя память, плюс процессорное время на вычисления при вызовах.
Виртуальные функции обычно дают оптимальный код — там, где они действительно необходимы.
Т.е. где требуется _сохранить_ некое условие, по которому в будущем _надо_выбрать_ тот или иной вариант поведения.
Проблема в том, что при использовании всяких фреймворков часто получается куча полиморфных объектов с тривиальной реализацией.
Т.е. сохранять условие не нужно, выбор одно из одного
Наверное, будущее за шаблонными классами, которые при желании можно использовать как полиморфные
Здравствуйте, Мишень-сан, Вы писали:
МС>Здравствуйте, DaStartuper, Вы писали:
DS>>Здравствуйте, ulu, Вы писали:
ulu>>>А почему интерфейс неприемлемо? Вообще-то, все так и делают.. DS>>Интерфейс — это виртуальные фукнции. Виртуальные функции — это лишняя память, плюс процессорное время на вычисления при вызовах.
МС>Это по указателю на каждый вирт. метод (для x86 — 4 байта) и 1(!!!) лишняя инструкция для вычисления адреса косвенного вызова на любом вменяемом компиляторе. МС>Неужели у вас эти объекты создаются в условиях жёсткой нагрузки сотнями тысяч и также зверски используются? МС>ИМХО накладные расходы на виртуальный вызов давно перестали быть проблемой.
+ v-table O(N) от числа объектов, размер еще зависит от прагмы про множественное наследование
+ передача параметров
+ невозможность инлайна и глобальной оптимизации
Здравствуйте, DaStartuper, Вы писали:
DS>Добрый день. Решил попробовать тестирование, и сразу напоролся на вопрос, который не знаю, как решить.
DS>Делаю тест в отдельном проекте.
DS>Имею примерно следующие файлы/классы (показано сразу содержимое .h и .cpp файлов):
... DS>Как организовать тестирование класса ObjProcessor? Когда я подключаю его .h-ник в проект теста, он автоматически тянет за собой файлы класса Obj, при попытке переопределения последнего, само собой, компилятор ругается на это переопределение. Можно, конечно, передавать в ObjProcessor интерфейс, а не класс, но это бывает неприемлемо. В то же время искусственное управление состоянием реального Obj тоже может быть невозможным, если он с какой-нить мудреной логикой. Как тут действовать тогда, что-то я не соображу.
погугли информацию про moc-объекты, это то, что тебе нужно. В тестах тебе надо сделать заглушку для класса, подсунув фейковую реализацию.
Например для сборки проекта работаю такие правила:
cc main.cpp -I./src src/A.cpp src/B.cpp
в тестах у тебя:
cc test.cpp -I./src src/A.cpp test/B.cpp
Есть ещё всякие фреймфорки типа CХХUnit попробуй посмотреть как там это делается.
МС>ИМХО накладные расходы на виртуальный вызов давно перестали быть проблемой.
размечтался. Как раз по мере уменьшения длинны конвейера они становятся все большей и большей проблемой
Есть решение без виртуальных вызовов — замена класса Obj в методе DoProcessing на boost::function. и использование boost::bind для того, чтобы подсунуть метод какого нибудь класса в качестве коллбека.
Есть недостатки у такого подхода. Если ObjProcessor использует достаточно много разных методов класса Obj, то передача такого числа коллбеков становится громоздкой и нарушается инкапсуляция функциональности. То есть появляется возможность часть колбеков передать из одного объекта, а часть из другого.
Зато можно легко писать тест.
DS>Добрый день. Решил попробовать тестирование, и сразу напоролся на вопрос, который не знаю, как решить.
DS>Делаю тест в отдельном проекте.
DS>Имею примерно следующие файлы/классы (показано сразу содержимое .h и .cpp файлов):
DS>
DS>#include"Obj.h"
DS>class ObjProcessor
DS>{
DS>public:
DS> int DoProcessing(const Obj& a);
DS>};
DS>int ObjProcessor::DoProcessing( const Obj& a )
DS>{
DS> if (a.Prop())
DS> return -1;
DS> return 0;
DS>}
DS>
DS>Как организовать тестирование класса ObjProcessor? Когда я подключаю его .h-ник в проект теста, он автоматически тянет за собой файлы класса Obj, при попытке переопределения последнего, само собой, компилятор ругается на это переопределение. Можно, конечно, передавать в ObjProcessor интерфейс, а не класс, но это бывает неприемлемо. В то же время искусственное управление состоянием реального Obj тоже может быть невозможным, если он с какой-нить мудреной логикой. Как тут действовать тогда, что-то я не соображу.
DS>P.S. Может, конечно, сегодняшний марафон виноват, и завтра мозг вместе с ногами очухается, будет чувствовать себя приемлемо и выдаст решение; а ну как нет?