I am trying to create my own boost::adaptors::transformed
.
Here is the related boost code.
Here is its usage (modified from a SO answer by LogicStuff):-
C funcPointer(B& b){ //"funcPointer" is function convert from "B" to "C" return instance-of-C } MyArray<B> test; //<-- any type, must already have begin() & end() for(C c : test | boost::adaptor::transformed(funcPointer)) { //... something .... }
The result will be the same as :-
for(auto b : test) { C c = funcPointer(b); //... something ... }
I created CollectAdapter
that aim to work like boost::adaptor::transformed
.
It works OK in most common cases.
Here is the full demo and back up. (same as below code)
The problematic part is CollectAdapter
- the core of my library.
I don't know whether I should cache the collection_
by-pointer or by-value.
CollectAdapter encapsulates underlying collection_
(e.g. pointer to std::vector<>
) :-
template<class COLLECTION,class ADAPTER>class CollectAdapter{ using CollectAdapterT=CollectAdapter<COLLECTION,ADAPTER>; COLLECTION* collection_; //<---- #1 problem? should cache by value? ADAPTER adapter_; //<---- = func1 (or func2) public: CollectAdapter(COLLECTION& collection,ADAPTER adapter){ collection_=&collection; adapter_=adapter; } public: auto begin(){ return IteratorAdapter< decltype(std::declval<COLLECTION>().begin()), decltype(adapter_)> (collection_->begin(),adapter_); } public: auto end(){ ..... } };
IteratorAdapter
(used above) encapsulates underlying iterator, change behavior of operator*
:-
template<class ITERATORT,class ADAPTER>class IteratorAdapter : public ITERATORT { ADAPTER adapter_; public: IteratorAdapter(ITERATORT underlying,ADAPTER adapter) : ITERATORT(underlying), adapter_(adapter) { } public: auto operator*(){ return adapter_(ITERATORT::operator*()); } };
CollectAdapterWidget
(used below) is just a helper class to construct CollectAdapter
-instance.
It can be used like:-
int func1(int i){ return i+10; } int main(){ std::vector<int> test; test.push_back(5); for(auto b:CollectAdapterWidget::createAdapter(test,func1)){ //^ create "CollectAdapter<std::vector<int>,func1>" instance //here, b=5+10=15 } }
The above code works OK in most cases, except when COLLECTION
is a temporary object.
More specifically, dangling pointer potentially occurs when I create adapter of adapter of adapter ....
int func1(int i){ return i+10; } int func2(int i){ return i+100; } template<class T> auto utilityAdapter(const T& t){ auto adapter1=CollectAdapterWidget::createAdapter(t,func1); auto adapter12=CollectAdapterWidget::createAdapter(adapter1,func2); //"adapter12.collection_" point to "adapter1" return adapter12; //end of scope, "adapter1" is deleted //"adapter12.collection_" will be dangling pointer } int main(){ std::vector<int> test; test.push_back(5); for(auto b:utilityAdapter(test)){ std::cout<< b<<std::endl; //should 5+10+100 = 115 } }
This will cause run time error. Here is the dangling-pointer demo.
In the real usage, if the interface is more awesome, e.g. use |
operator, the bug will be even harder to be detected :-
//inside "utilityAdapter(t)" return t|func1; //OK! return t|func1|func2; //dangling pointer
How to improve my library to fix this error while keeping performance & robustness & maintainablilty near the same level?
In other words, how to cache data or pointer of COLLECTION
(that can be adapter or real data-structure) elegantly?
Alternatively, if it is easier to answer by coding from scratch (than modifying my code), go for it. :)
The current code caches by pointer.
The main idea of workarounds is to cache by value instead.
Let adapter cache the value of COLLECTION
.
Here is the main change:-
COLLECTION collection_; //<------ #1 //changed from .... COLLECTION* collection_;
Disadvantage:-
std::vector
) will be value-copied - waste resource.std::vector
directly)I will create 2 versions of the library - AdapterValue
and AdapterPointer
.
I have to create related classes (Widget
,AdapterIterator
,etc.) as well.
AdapterValue
- by value. (designed for utilityAdapter()
)AdapterPointer
- by pointer. (designed for std::vector
)Disadvantage:-
I may use template specialization that do this :-
If( COLLECTION is an "CollectAdapter" ){ by value } Else{ by pointer }
Disadvantage:-
Sorry for very long post.
The structure pointer operator -> can be overloaded as a nonstatic class member function. The overloaded structure pointer operator is a unary operator on its left operand. The argument must be either a class object or a reference of this type.
A user-defined type can overload a predefined C# operator. That is, a type can provide the custom implementation of an operation in case one or both of the operands are of that type. The Overloadable operators section shows which C# operators can be overloaded.
Library Overrides is a system designed to replace and supersede Proxies. Most types of linked data-blocks can be overridden, and the properties of those overrides can then be edited. When the library data changes, unmodified properties of the overridden one will be updated accordingly.
If two iterators point to different elements in a container, then they are not equal. The first two template operators return true only if both left and right store the same iterator. The third template operator returns true only if both left and right store the same stream pointer.
I personally would go with template specialisation – however, not specialise the original template, but a nested class instead:
template<typename Collection, typename Adapter> class CollectAdapter { template<typename C> class ObjectKeeper // find some better name yourself... { C* object; public: C* operator*() { return object; }; C* operator->() { return object; }; }; template<typename C, typename A> class ObjectKeeper <CollectAdapter<C, A>> { CollectAdapter<C, A> object; public: CollectAdapter<C, A>* operator*() { return &object; }; CollectAdapter<C, A>* operator->() { return &object; }; }; ObjectKeeper<Collection> keeper; // now use *keeper or keeper-> wherever needed };
The outer class then covers both cases by just always using pointers while the nested class hides the differences away.
Sure, incomplete (you yet need to add appropriate constructors, for instance, both to outer and inner class), but it should give you the idea...
You might even allow the user to select if she/he wants to copy:
template<typename Collection, typename Adapter, bool IsAlwaysCopy = false> class CollectAdapter { template<typename C, bool IsCopy> class ObjectWrapper // find some better name yourself... { C* object; public: C* operator*() { return object; }; C* operator->() { return object; }; }; template<typename C> class ObjectWrapper<C, true> { C object; public: C* operator*() { return &object; }; C* operator->() { return &object; }; }; // avoiding code duplication... template<typename C, bool IsCopy> class ObjectKeeper : public ObjectWrapper<C, IsCopy> { }; template<typename C, typename A, bool IsCopy> class ObjectKeeper <CollectAdapter<C, A>, IsCopy> : public ObjectWrapper<CollectAdapter<C, A>, true> { }; ObjectKeeper<Collection> keeper; };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With