Статический полиморфизм CRTP: возможно ли заменить базовый класс на макет?
Я использую статический полиморфизм CRTP на сервере веб-сокетов, чтобы отделить сетевой код от бизнес-логики. Базовый класс вызывает методы для обработки производных сообщений, а производная в свою очередь вызывает базу для отправки / получения. Это работает как шарм, выглядит примерно так:
template<typename Derived>
class WebsocketSessionBase {
// basic websocket send/receive stuff
void someBaseMethod() {
static_cast<Derived*>(this)->otherDerivedMethod();
}
};
class WebsocketSession : public WebsocketSessionBase<WebsocketSession> {
// specific business logic
void someDerivedMethod() {
otherBaseMethod();
}
};
Сейчас идет юнит-тестирование. Так как код отделен, я хотел бы проверить функциональность в классах отдельно.
Тестирование базового класса просто:
class TestSession : public WebsocketSessionBase<TestSession> {
// same interface as WebsocketSession, but test code, cool!
};
Но как мне проверить производный класс? Мне пришло в голову добавить параметр шаблона Base, что делает тестовый код нормальным (Base - это фиктивный класс). Но я получаю 2 шаблона классов, ссылающихся друг на друга в рабочей версии...:(
template<typename Base>
class TestableWebsocketSession : public Base {
};
using TestedWebsocketSession = TestableWebsocketSession<MockBase>;
using ProdWebSocketSession = TestableWebsocketSession<WebsocketSessionBase<... // infinite loop - now what!?
Можно ли прийти к этому?
1 ответ
Я не знаю, стоит ли это того, но вы могли бы сделать WebsocketSession шаблоном класса, принимающим параметр шаблона:
template<class T>
struct WebsocketSessionBase { /*...*/ };
template<template<class> class B>
struct WebsocketSessionDerived: B<WebsocketSessionDerived<B>>{ /*...*/ };
using WebsocketSession = WebsocketSessionDerived<WebsocketSessionBase>;
using DerivedTestSession = WebsocketSessionDerived<WebsocketSessionMockBase>;
struct BaseTestSession : WebsocketSessionBase<BaseTestSession>{ /*...*/ };