├── .clang-format ├── README.md ├── qaop.h └── test ├── test_case ├── extended_sample1 │ ├── Makefile │ ├── client.cpp │ ├── config.hpp │ └── queue.hpp ├── extended_sample2 │ ├── Makefile │ ├── client.cpp │ ├── config.hpp │ └── queue.hpp ├── extended_sample3 │ ├── Makefile │ ├── client.cpp │ ├── config.hpp │ └── queue.hpp ├── extended_sample4 │ ├── Makefile │ ├── client.cpp │ ├── config.hpp │ └── queue.hpp └── origin │ ├── Makefile │ ├── client.cpp │ ├── config.hpp │ └── queue.hpp ├── test_queue2.cc └── test_static.cc /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Right 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: MultiLine 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | AfterExternBlock: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Attach 40 | BreakBeforeInheritanceComma: false 41 | BreakInheritanceList: BeforeColon 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 80 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 8 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: false 72 | IndentPPDirectives: None 73 | IndentWidth: 4 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBinPackProtocolList: Auto 83 | ObjCBlockIndentWidth: 2 84 | ObjCSpaceAfterProperty: false 85 | ObjCSpaceBeforeProtocolList: true 86 | PenaltyBreakAssignment: 2 87 | PenaltyBreakBeforeFirstCallParameter: 19 88 | PenaltyBreakComment: 300 89 | PenaltyBreakFirstLessLess: 120 90 | PenaltyBreakString: 1000 91 | PenaltyBreakTemplateDeclaration: 10 92 | PenaltyExcessCharacter: 1000000 93 | PenaltyReturnTypeOnItsOwnLine: 60 94 | PointerAlignment: Left 95 | ReflowComments: true 96 | SortIncludes: true 97 | SortUsingDeclarations: true 98 | SpaceAfterCStyleCast: false 99 | SpaceAfterTemplateKeyword: true 100 | SpaceBeforeAssignmentOperators: true 101 | SpaceBeforeCpp11BracedList: false 102 | SpaceBeforeCtorInitializerColon: true 103 | SpaceBeforeInheritanceColon: true 104 | SpaceBeforeParens: ControlStatements 105 | SpaceBeforeRangeBasedForLoopColon: true 106 | SpaceInEmptyParentheses: false 107 | SpacesBeforeTrailingComments: 1 108 | SpacesInAngles: false 109 | SpacesInContainerLiterals: true 110 | SpacesInCStyleCastParentheses: false 111 | SpacesInParentheses: false 112 | SpacesInSquareBrackets: false 113 | Standard: Cpp11 114 | TabWidth: 4 115 | UseTab: Never 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qaop 2 | QAOP is a header only C++ AOP framwork. 3 | 4 | The very intuitive AOP implemention is the template subclass pattern: 5 | ``` 6 | template 7 | class AspectA : public _Base { 8 | ... 9 | }; 10 | 11 | template 12 | class AspectB : public _Base { 13 | ... 14 | }; 15 | 16 | class Base { 17 | ... 18 | }; 19 | 20 | typedef AspectB > Combined; 21 | ``` 22 | However, there are some problems with this solution. 23 | 24 | 1. You could only return the _Base * pointer or reference. 25 | Because for each Aspect, the final class info is unkown, and you can not pass the fulltype as a template parameter, because the combined fulltype defination needs the aspects. 26 | This is a typical "Chick or Egg" problem. Just as Hugo Arregui demostrate in ACCU paper(He used the CRTP to solve the problem). see https://github.com/hugoArregui/CPP_AOP-CRTP 27 | 2. If you have static varibables in the Apsect templates, it will be hard to give a easy way to initialize it. 28 | Though the public static member is inherited, but for initialization, one must give out the full instance for the template. And this means, you can not write like this : 29 | ```c++ 30 | template<> Combined::static_memberA(xxx); 31 | ``` 32 | but have to write like this 33 | 34 | ```c++ 35 | template<> Combined::AspectB::AspectA::static_memberA(xxx); 36 | ``` 37 | 38 | 3. Generally you may have to use specific virtual function to realize the jointpoint, which would be very hard to port to other classes. 39 | 40 | ## QAOP will make all these things simpler and intuitive. 41 | 42 | The aspects will be quite similar to prevous ones, just like this: 43 | ```c++ 44 | template 45 | 46 | class AspectA : public _Base::this_t { 47 | using _Base::fulltype_t; 48 | ... 49 | static static_proxy sp; 50 | static int get_a() { 51 | return sp.template proxy(); 52 | } 53 | 54 | std::function AdviceA ( std::function & f) { 55 | ... 56 | } 57 | 58 | double ActionA ( int a, int b, char * c) { 59 | ... 60 | } //suppose base::foo has interface foo (int, int, char *) 61 | 62 | ``` 63 | And in the client code, just using the following code to do the initialization and insertation of jointpoints and advices: 64 | 65 | ```c++ 66 | typedef Decorate::with::type CombinedType; 67 | void initialaztion{ 68 | proxy(5); 69 | static action a1(stub::wrap(CombinedType::ActionA)); 70 | static action a2(stub::_(&Base::foo), stub::wrap(&AdviceA)); 71 | static action a3(stub::_(&Base::foo, [](){std::cout<<"Let's try some Lambda!"< 5 | #include 6 | 7 | namespace qaop { 8 | 9 | // TODO: we can use some fancy macros to make the syntax more compact. 10 | 11 | // Writing Aspect could be quite easy and intuitive. 12 | 13 | /*/// Aspect example: 14 | template 15 | struct AspectFoo : public _Base::this_t { // here is the only different part 16 | // from ordinary CRTP 17 | using fulltype_t = typename _Base::fulltype_t; 18 | using this_t AspectFoo; 19 | 20 | AspectFoo() { 21 | std::cout << "ctor for aspect " << typeid(this_t).name() 22 | << ", fulltype is " << typeid(fulltype_t).name() 23 | << std::endl; 24 | } 25 | 26 | // Aspect Member function example 27 | // Member function which operate on fulltype_t 28 | virtual fulltype_t somefunc(fulltype_t a) { } 29 | 30 | // Aspect interface 31 | virtual void funcFoo() { 32 | std::cout << "greeting from " << typeid(&funcfoo).name() 33 | << std::endl; 34 | } 35 | 36 | virtual ~AspectFoo() { 37 | std::cout << "dtor for aspect " << typeid(this_t).name() 38 | << ", fulltype is " << typeid(fulltype_t).name() 39 | << std::endl; 40 | } 41 | // static member support 42 | static qaop::static_proxy sp; 43 | }; 44 | //*/// 45 | 46 | // It is worth noting that fulltype_t will keep the same for all 47 | // apects. It is a MUST-BE because all aspect should work on same 48 | // type, instead of lots of multiple inheritace interface. 49 | 50 | // thus member functions should be designed to operate on fulltype_t, 51 | // which will make the design keep clean and neat. 52 | 53 | // Another Important thing is, AopEnhancedClass ISA OriginalClass. 54 | // That is, if you OriginalClass as pointer in the framework, 55 | // AopEnhancedClass could just fit well. 56 | 57 | // Usage: 58 | // using AopEnhancedClass = qaop::Decorate::with::type; 60 | // AopEnhancedClass instance; 61 | // instance.funcFoo(); 62 | // instance.funcBar(); 63 | // instance.funcBaz(); 64 | 65 | // You can also combine multiple apsects into one, thus reuse them more easily. 66 | 67 | // Usage: 68 | // template 69 | // using AspectFooBar = qaop::Combine::combined<_Base>; 70 | // using CombinedAopClass = qaop::Decorate::with::type; 72 | 73 | // In order to support static member in CombinedAopClass (Not aspect template) 74 | // we use a static member proxy. 75 | // or called sp, which makes the interface simpler. 76 | 77 | // template < typename _Base > 78 | // struct AspectFoo: public _Base::this_t { 79 | // using fulltype_t = _Base::fulltype_t; 80 | // using this_t = AspectFoo; 81 | // ... 82 | // static qaop::static_proxy sp; 83 | // }; 84 | 85 | // Usage: 86 | // using AopClassFooBar = qaop::Decorate::with::type; 88 | // using AopClassFooBaz = qaop::Decorate::with::type; 90 | 91 | // In global initialization (e.g. in main or global class ctor), do this : 92 | // void Initialization { 93 | // qaop::static_member(initialization 94 | // parameters); 95 | // _OR_ fulltype::sp.template proxy(initialization 97 | // parameters); 98 | 99 | // qaop::static_member(0.5467f); 101 | // AopClassFooBaz::sp.template proxy(0.5201314f); 103 | // } 104 | 105 | /*/// now you can use them just as like class static member 106 | auto& staticMemberFooBar = 107 | AopClassFooBar::sp 108 | .template proxy(); 109 | auto& staticMemberFooBaz = 110 | AopClassFooBaz::sp 111 | .template proxy(); 112 | //*/// 113 | 114 | // Enhancement original class could be divide into two categories: 115 | // 1. add new member function and new static members for class. 116 | // 2. enhance original member fucntions. 117 | 118 | // the first aim could be easily achieved by previous design. and 119 | // the second aim would need more toys to play with. 120 | 121 | // Among the consepts of AOP, jointpoint plays an important role, in C++, 122 | // Jointpoints means virtual member function call. Another key concept in 123 | // AOP is advice, which are things done at jointpoints. 124 | 125 | // For each jointpoint, we could insert advice. the advice could be put 126 | 127 | // 1. before jointpoint 128 | // 2. around jointpoint 129 | // 3. after jointpoint 130 | 131 | // for before case, advices should behave like stack, last inject, first exec 132 | // for after case, advices should behave like pipe, first inject, first exec 133 | // for around case, advices should be exectuted in a in hieratic callback way 134 | 135 | // As for advice, if you have an idea of python Decorator (@Decorator) 136 | // syntax sugar, advice is quite like its counterpart in AOP. 137 | 138 | // By design we may want some advices have same signature with original 139 | // member function, and we call this kind of advice "action". 140 | 141 | // on the other side, we also want some advices are interface-insensitve with 142 | // original member function, but have the abitity to access resources outside 143 | 144 | // After that we need to inject the advices into specific jointpoints 145 | // , that is, some virtual member function of original class. 146 | // we call this proces waven. 147 | 148 | // In qaop, one advice is an aop member function, it act as higher level 149 | // fucntor which accept original class member function and return a new 150 | // wraped function, the later could be invoked by aop-decorated class, 151 | // in a virtual function call way. 152 | 153 | // Here is an simple example of advices 154 | /* 155 | template < typename _Base > 156 | struct AspectCount : public _Base::this_t { 157 | using this_t = AspectCount; 158 | using base_t = _Base::this_t; 159 | using fulltype_t = _Base::fulltype_t; 160 | using func_t = std::function ; 161 | 162 | AspectCount() : counter(0) { } 163 | func_t adviceIncrease (func_t &f) { 164 | return [=]() { 165 | f(); 166 | this->counter++; 167 | return 0; 168 | }; 169 | } 170 | func_t adviceDecrease (func_t &f) { 171 | return [=]() { 172 | f(); 173 | if(counter > 0) 174 | this->counter--; 175 | return 0; 176 | }; 177 | } 178 | int count() const {return counter;} 179 | 180 | int counter; 181 | }; // struct AspectCount 182 | */ 183 | 184 | /* 185 | 186 | */ 187 | 188 | // If you have no intrests in how it works, that's all what you need to know. 189 | // If you want to run after the rabbit, come with me ... 190 | //*/ 191 | 192 | // forward declarition. aopfy will reconstruct the series of type after 193 | // iteration, and put it into fulltype_t; 194 | template class... __Aspects> 195 | struct aopfy; 196 | 197 | // parameter base wrap.Only if we have a base with the template parameters, we 198 | // can reconstruct the fulltype. 199 | template class... __Aspects> 200 | struct aopbase { 201 | // reference_t will keep the same during iteration. 202 | using reference_t = aopfy; 203 | using base_t = _Base; 204 | }; 205 | 206 | // iteration template defination. 207 | template class __FirstAspects, 208 | template class... __RestAspects> 209 | struct aopfy<_AopBase, __FirstAspects, __RestAspects...> { 210 | using fulltype_t = __FirstAspects< 211 | typename aopfy<_AopBase, __RestAspects...>::fulltype_t>; 212 | using this_t = typename _AopBase::base_t; 213 | }; 214 | 215 | // end of iteration tempalte defination. 216 | template struct aopfy<_AopBase> { 217 | using fulltype_t = 218 | typename _AopBase::reference_t; // magic begins from here. 219 | using this_t = 220 | typename _AopBase::base_t; // this is why the reconstuction works. 221 | // Aspects should only inherate from 222 | // this_t, not the aopfy<> class. 223 | }; 224 | 225 | // In fact, you can use AOP just by the upper codes 226 | // example: using Combined = Aspect1, Aspect1, Aspect2, Aspect3 > > > >::fulltype_t; however, it 228 | // seems weired, though it will be easier to show how it works. 229 | 230 | // Struct Decorate makes it easy to use. 231 | template struct Decorate { 232 | // keep the reference during iteration. 233 | template