2016-12-09 10 views
4

У меня есть следующая программа, аналогичная моей кодовой базе. Класс FunctionState, который выполняет какой-то алгоритм (потенциально в нескольких потоках) и класс Function, который контролирует использование классов FunctionState и может выполнять некоторые операции установки/удаления алгоритма.Как я могу использовать CRTP для удаления виртуального метода в этом контексте?

#include <iostream> 
#include <vector> 

class FunctionState; 

class Function { 
public: 
    virtual FunctionState* NewFunctionState() = 0; 

protected: 
    std::vector<FunctionState*> states; 
}; 

class FunctionState { 
public: 
    FunctionState(Function* func) : mFunc(func) {} 

    virtual void RunState() = 0; 
    void ExecuteFunctionLotsAndLotsOfTimes(); 

private: 
    Function* mFunc; 
}; 

#define VERY_BIG_NUMBER 10 

void FunctionState::ExecuteFunctionLotsAndLotsOfTimes() { 
    for(int i = 0; i < VERY_BIG_NUMBER; ++i) { 
     RunState(); 
    } 
}; 

class PrintFunction : public Function { 
    FunctionState* NewFunctionState(); 
}; 

class PrintFunctionState : public FunctionState { 
public: 
    PrintFunctionState(PrintFunction* func) : FunctionState(func) {} 

    void RunState() override { 
     std::cout << "in print function state" << '\n'; 
    } 
}; 

FunctionState* PrintFunction::NewFunctionState() { 
    FunctionState* state = new PrintFunctionState(this); 
    states.push_back(state); 
    return state; 
} 

class AddFunction : public Function { 
    FunctionState* NewFunctionState(); 
}; 

class AddFunctionState : public FunctionState { 
public: 
    AddFunctionState(AddFunction* func) : FunctionState(func), x(0) {} 

    void RunState() override { 
     ++x; 
    } 
private: 
    int x; 
}; 

FunctionState* AddFunction::NewFunctionState() { 
    FunctionState* state = new AddFunctionState(this); 
    states.push_back(state); 
    return state; 
} 


int main() { 
    Function* func = new PrintFunction(); 
    Function* func2 = new AddFunction(); 
    std::vector<Function*> vec = {func, func2}; 

    for(auto& func : vec) { 
     func->NewFunctionState()->ExecuteFunctionLotsAndLotsOfTimes(); 
    } 

    return 0; 
} 

Теперь я профилированный мой код, и увидел, что есть точка доступ в FunctionState :: ExecuteFunctionLotsAndLotsOfTimes(). Проблема в том, что эта функция повторяется много раз и вызывает RunState(), виртуальную функцию в классе FunctionState. Там я выполняю много операций, которые потенциально могут вывести указатели vtable из кеша L1, в результате чего кеш L1 пропускает каждую итерацию цикла.

Поэтому я хочу удалить необходимость в виртуальном вызове. Я решил, что хороший способ сделать это был с CRTP. Класс FunctionState будет принимать параметр шаблона типа реализующего его класса и называть его соответствующим методом, при этом виртуальный вызов RunState() не требуется.

Теперь, когда я пытался сделать переместить его в CRTP, я столкнулся с некоторыми проблемами с классом функций:

  1. Как я вперед объявить класс FunctionState (как это шаблонный сейчас)?
  2. Должен ли я добавить параметр шаблона в класс Function?

    3. Какова была бы конструкция объекта Function, если я его шаблон? Как удалить необходимость в классах, которые используют объект Function для указания параметра типа?

Пожалуйста, обратите внимание, что это всего лишь тривиальный вариант моей реальной кодовую. Реальная кодовая база - 10 К + строк кода (не неуправляемая, но полного переписывания не может быть и речи).

Также, если есть другой способ удалить виртуальный вызов RunState(), который не включает CRTP, тогда это также будет оценено.

Моя попытка использовать CRTP:

#include <iostream> 
#include <vector> 

class Function; 

template<class T> 
class FunctionState { 
public: 
    FunctionState(Function* func) : mFunc(func) {} 

    void RunState() { 
     static_cast<T*>(this)->RunState(); 
    }; 

    void ExecuteFunctionLotsAndLotsOfTimes(); 
private: 
    Function* mFunc; 
}; 

class Function { 
public: 
    virtual FunctionState* NewFunctionState() = 0; 

protected: 
    std::vector<FunctionState*> states; 
}; 

#define VERY_BIG_NUMBER 10 

template <typename T> 
void FunctionState<T>::ExecuteFunctionLotsAndLotsOfTimes() { 
    for(int i = 0; i < VERY_BIG_NUMBER; ++i) { 
     RunState(); 
    } 
}; 

class PrintFunctionState; 
class PrintFunction : public Function { 
    PrintFunctionState* NewFunctionState(); 
}; 

class PrintFunctionState : public FunctionState<PrintFunctionState> { 
public: 
    PrintFunctionState(PrintFunction* func) : FunctionState<PrintFunctionState>(func) {} 

    void RunState() { 
     std::cout << "in print function state" << '\n'; 
    } 
}; 

PrintFunctionState* PrintFunction::NewFunctionState() { 
    PrintFunctionState* state = new PrintFunctionState(this); 
    states.push_back(state); 
    return state; 
} 

class AddFunctionState; 
class AddFunction : public Function { 
    AddFunctionState* NewFunctionState(); 
}; 

class AddFunctionState : public FunctionState<AddFunctionState> { 
public: 
    AddFunctionState(AddFunction* func) : FunctionState<AddFunctionState>(func), x(0) {} 

    void RunState() { 
     ++x; 
    } 
private: 
    int x; 
}; 

AddFunctionState* AddFunction::NewFunctionState() { 
    AddFunctionState* state = new AddFunctionState(this); 
    states.push_back(state); 
    return state; 
} 


int main() { 
    Function* func = new PrintFunction(); 
    Function* func2 = new AddFunction(); 
    std::vector<Function*> vec = {func, func2}; 

    for(auto& func : vec) { 
     func->NewFunctionState()->ExecuteFunctionLotsAndLotsOfTimes(); 
    } 

    return 0; 
} 
+1

_ «Теперь, когда я попытался переместить его в CRTP ...» _ Можете ли вы показать нам вашу точную попытку, пожалуйста? –

+0

Добавил мою попытку, она не компилируется из-за проблем с использованием FunctionState без аргумента шаблона. – Andrew

+0

Ну, вам нужен аргумент шаблона при создании экземпляра класса шаблона. В случае CRTP - производный класс. И что? –

ответ

2

Как насчет смешанное решение на основе стирания стилей и CRTP?
следует, минимальный, рабочий пример, основанный на фрагменте кода в вопросе:

#include <iostream> 
#include <vector> 

class PrintFunctionState; 
class AddFunctionState; 
class FunctionState; 

class Function { 
    template<typename T> 
    static FunctionState * InternalNewFunctionState(Function *self, std::vector<FunctionState*> &states) { 
     FunctionState* state = new T(self); 
     states.push_back(state); 
     return state; 
    } 

public: 
    template<typename T> 
    static Function * create() { 
     Function *func = new Function; 
     func->internalNewFunctionState = &InternalNewFunctionState<T>; 
     return func; 
    } 

    FunctionState* NewFunctionState() { 
     return internalNewFunctionState(this, states); 
    } 

private: 
    FunctionState * (*internalNewFunctionState)(Function *, std::vector<FunctionState*> &); 
    std::vector<FunctionState*> states; 
}; 

class FunctionState { 
public: 
    FunctionState() = default; 
    virtual ~FunctionState() = default; 
    virtual void ExecuteFunctionLotsAndLotsOfTimes() = 0; 
}; 

template<typename Derived> 
class IntermediateFunctionState: public FunctionState { 
public: 
    IntermediateFunctionState(Function* func) : mFunc(func) {} 

    void ExecuteFunctionLotsAndLotsOfTimes() override { 
     Derived *self = static_cast<Derived *>(this); 
     for(int i = 0; i < 10; ++i) { 
      self->RunState(); 
     } 
    } 

private: 
    Function* mFunc; 
}; 

class PrintFunctionState : public IntermediateFunctionState<PrintFunctionState> { 
public: 
    PrintFunctionState(Function* func) : IntermediateFunctionState(func) {} 

    void RunState() { 
     std::cout << "in print function state" << '\n'; 
    } 
}; 

class AddFunctionState : public IntermediateFunctionState<AddFunctionState> { 
public: 
    AddFunctionState(Function* func) : IntermediateFunctionState(func), x(0) {} 

    void RunState() { 
     std::cout << "in add function state" << '\n'; 
     ++x; 
    } 

private: 
    int x; 
}; 

int main() { 
    Function* func = Function::create<PrintFunctionState>(); 
    Function* func2 = Function::create<AddFunctionState>(); 
    std::vector<Function*> vec = { func, func2 }; 

    for(auto& func : vec) { 
     func->NewFunctionState()->ExecuteFunctionLotsAndLotsOfTimes(); 
    } 

    return 0; 
} 

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

0

Function должен иметь тип без шаблона в качестве возвращаемого значения NewFunctionState, так что вам нужен дополнительный базовый класс

class FunctionStateBase { 
    virtual void ExecuteFunctionLotsAndLotsOfTimes() = 0; 
    // No void RunState()! 
} 

template<typename T> 
class FunctionState { 
    void ExecuteFunctionLotsAndLotsOfTimes(); 
    // Still no void RunState()! 
} 

class PrintFunctionState : public FunctionState<PrintFunctionState> { 
    void RunState(); 
} 

template <typename T> 
void FunctionState<T>::ExecuteFunctionLotsAndLotsOfTimes() { 
    for(int i = 0; i < VERY_BIG_NUMBER; ++i) { 
     static_cast<T*>(this)->RunState(); // Statically bound! 
    } 
}; 
+0

Спасибо за ответ. Проблема в том, что когда я использую свою функцию, мне придется перейти в реализующий тип FunctionState, что приведет к поражению полиморфизма наличия интерфейса Function. Например, уже невозможно иметь набор функций, т. Е. Std :: vector funcs. Отредактировал мой вопрос, чтобы включить этот пример. – Andrew

+0

Yup, нужно идти в другую сторону, тогда – Caleth

0

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

#include <iostream> 

/* 
    Just make print invocations a little less cluttered for our purposes here. 
*/ 
template <typename Type> 
void Show(Type value) 
{ 
    std::cout << value << std::endl; 
} 

/* 
    Base class for function types 
*/ 
template <typename Self> 
class Function 
{ 
    public: 

/* 
    For the best performance possible, we'll always inline this function. 
*/  
    inline void RunState() 
    { 
     static_cast<Self*>(this)->RunState(); 
    } 

    void ExecuteFunctionLotsAndLotsOfTimes(int iterations = 1) 
    { 
     for(int i = 0; i < iterations; ++i) 
     { 
      Show("...Loop...");  
      RunState(); 
     }  
    } 
}; 

/* 
    Everything here is placed in an internal namespace, as none of it will be used by the caller. 
*/ 
namespace Internal_ 
{ 

/* 
    ChainFunctionLink works like an array of functions. Each of it's members 
    is either some kind of function object or another ChainFunctionLink. 
*/  
template <typename First, typename Second> 
struct ChainFunctionLink : Function<ChainFunctionLink<First, Second>> 
{ 
    ChainFunctionLink(First first, Second second) 
    : first(first), second(second) 
    { } 

    inline void RunState() 
    { 
     first.RunState(); 
     second.RunState(); 
    } 

    First 
     first; 
    Second 
     second; 
}; 

/* 
    We won't be able to explicitly specify the template parameters of ChainFunctionLink 
    later, so a generating function will be needed to deduce them for us. 
*/ 
template <typename First, typename Second> 
ChainFunctionLink<First, Second> MakeChainFunctionLink(First first, Second second) 
{ 
    return ChainFunctionLink<First, Second>(first, second); 
} 

} // namespace Internal_ 

/* 
    ChainFunction generates ChainFunctionLink's for the caller. 
*/ 
template <typename First, typename Second, typename ...Next> 
auto ChainFunction(First first, Second second, Next ...next) 
{ 
    return Internal_::MakeChainFunctionLink(first, ChainFunction(second, next...)); 
} 

/* 
    The last link in the chain. 
*/ 
template <typename Last> 
Last ChainFunction(Last last) 
{ 
    return last; 
} 

// Example usage: 

class PrintFunction : public Function<PrintFunction> 
{ 
    public: 

    inline void RunState() 
    { 
     Show("PrintFunction::RunState()"); 
    }  
}; 

class AddFunction : public Function<AddFunction> 
{ 
    public: 

    inline void RunState() 
    { 
     Show("AddFunction::RunState()"); 
    }   
}; 

int main() 
{ 
    auto 
     chain = ChainFunction(AddFunction(), AddFunction(), AddFunction(), PrintFunction()); 
    chain.ExecuteFunctionLotsAndLotsOfTimes(4);  
} 

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

РЕДАКТИРОВАТЬ

Существует один дополнительное предостережение к этому конкретному виду реализации: цепочечные функции хранятся по значению. Если вы хотите этого избежать, просто переформулируйте ChainFunctionLink членов first и second в качестве ссылок. Не позволит вам передавать временные параметры в качестве параметров на ChainFunction, хотя, конечно ...