📄 rationale.qbk
字号:
well designed and manageable class, unfortunately, C++ does not guaranteelayout-compatibility almost in any case. For example, the C++ standard doesnot guarantee that the classes `struct{T1 a; T2 b;}` and `struct{T1 b; T2 a;}`are layout-compatible, and therefore the trick of reinterpret_cast is anundefined behavior. I am with you in which that in the 100% of the casesthis scheme will really work, but the standard is the standard. If you canlook the layout-compatibility subject in it (http://www.kuzbass.ru/docs/isocpp/).As you see, sometimes the standard is cruel. Although mutant seems a lost case,please do not hurry to eliminate it. We will see what can be done for it.]][*Matias][:['I read the standard, and you were right about it. Mutant was an implementationdetail. It is a pity because I am sure that it will work perfect in any compiler.Perhaps the standard becomes more strict some day and mutant returns to life...We can then try a wrapper around a relation<A,B> that have two references namedfirst and second that bind to A and B, or B and A.]]``relation<TA,TB> r;const_reference_pair<A,B> pba(r);const_reference_pair<B,A> pbb(r);``[:['It is not difficult to code the relation class in this way but two referencesare initialized with every access and the use of `pba.first` will be slowerthan `r.left` in most compilers. It is very difficult to optimize this kind ofreferences.]][*Joaquin][:['This workaround is not possible, due to technical problems withthe expected behavior of the iterators. If the iterators of bm.left are ofbidirectional type, then standard stated that it have to return an object of typeconst value_type& when dereferenced. You will have to return a const_reference_paircreated in the flight, making it impossible to return a reference.]][*Matias][:['I understand... I have workaround for that also but surely the standard will attack me again! We must manage to create the class relationthat responds as we want, the rest of the code will flow from this point.This clear separation between the relation class and the rest of the library,is going to help to us to separate the problems and to attack them better.]][*Joaquin][:['What workaround? It already pricks my curiosity,I have dedicateda long time to the subject and I do not find any solution except that weallow the relation class to occupy more memory.]][*Matias][:['We must achieve that the relation<A,B> size equals the pair<A,B> sizeif we want this library to be really useful. I was going to write my workaround andI realized that It does not work. Look at this:http://www.boost.org/libs/iterator/doc/new-iter-concepts.htmlBasically the problem that we are dealing is solved if we based our iterators onthis proposal. The present standard forces that the bidirectional iterators alsoare of the type input and output. Using the new concepts there is no inconvenientin making our iterators "Readable Writable Swappable Bidirectional Traversal".Therefore the const_reference_pair returns to be valid.]][*Joaquin][:['It is correct in the sense that you simply say thatyour iterators are less powerful than those of the std::map. It isnot that it is wrong, simply that instead of fixing the problem, youconfess it. ]][*Matias][:['OK, but in our particular case; What are the benefitsof offering a LValue iterator against a Read Write iterator?It does not seem to me that it is less powerful in this case. ]][*Joaquin][:['The main problem with a ReadWrite is that the following thing:`value_type * p=&(*it);`fails or stores a transitory direction in p. Is this important in the real life?I do not know. How frequently you store the direction of the elements of a map?Perhaps it is not very frequent, since the logical thing is to store theiterators instead of the directions of the elements.Let us review our options:]][:['1. We used mutant knowing that is not standard, but of course it issupported in the 100% of the cases.]][:['2. We used const_reference_pair and we declared the iterators not LValue.]][:['3. We found some trick that still we do not know. I have thus been playingwith unions and things, without much luck.]][:['4. We leverage the restriction that views have to support the first, secondnotation. If we made this decision, there are several possibilities:]][:['''' '''a. The left map has standard semantics first/second while the right maphas the inverse semantics.]][:['''' '''b. Instead of first and second we provide first() and second(), withwhich the problem is trivial.]][:['''' '''c. The map view do not support first/second but left/right as thefather relation]][:['5. We solve the problem using more memory than sizeof(pair<A,B>).]][:['In any case, I would say that the only really unacceptable option is the last one.]][*Matias][:['Lets see.]][:['1. I want the "standard compliant" label in the library.]][:['2. This is the natural choice, but knowing that there is another optionthat always works and it is more efficient is awful.]][:['3. I have also tried to play with unions, the problem is that the union membersmust be POD types.]][:['4. This option implies a big lost to the library.]][:['5. Totally agree.]][:['I want to add another option to this list. Using metaprogramming,the relation class checks if the compiler supports the mutant idiom.If it supports it then it uses it and obtains zero overheadplus LValue iterators, but if it do not supports it then usesconst_reference_pair and obtains minimum overhead with ReadWrite iterators.This might be controversial but the advantages that mutant offers are very bigand the truth is that I do not believe that in any actual compiler this idiom isnot supported. This scheme would adjust perfectly to the present standardsince we are not supposing anything. The only drawback here is that althoughthe mutant approach allows to make LValue iterators we have to degrade theyto Read Write in both cases, because we want that the same code can becompiled in any standard compliant compiler.]][^- Hopefully we find our way out of the problem -][*Joaquin][:['Changing the subject, I believe that the general concept of hooking datais good, but I do not like the way you implement it. It has to be easyto migrate to B.MI to anticipate the case in that Boost.Bimap becomes insufficient.It is more natural for a B.MI user that the data is accessed without the indirectionof `.data`. I do not know how this can be articulated in your framework.]][*Matias][:['I have a technical problem to implement the data_hook in this way.If the standard would let us use the mutant idiom directly, I can implement itusing multiple inheritance. But as we must use const_reference_pair too, It becomesimpossible for me to support it. We have three options here:]][:['1) relation { left, right, data } and pair_view { first, second, data }]][:['- This is more intuitive within the bimap framework, since it does notmix the data with the index, as a table in a data base does, but gives more importance tothe index.]][:['- It is not necessary that the user puts the mutable keyword in each member ofthe data class.]][:['- This moves away just a little bit from B.MI because the modelof it is similar to a table, but it continues to exist a clear path of migration.]][:['2) relation { left,right, d1,d2... dn } and pair_view { first, second, data }]][:['- The path to B.MI is the one you have proposed.]][:['- It is very asymmetric. It is necessary to explain that the views arehandled different that the relation.]][:['- The user must place the mutable keyboards in the data class.]][:['3) Only relation { left,right, d1,d2... dn }]][:['- Simple migration path to B.MI.]][:['- You are not able to access the hooked data from the views.]][:['My vote goes to the first proposal.]][*Joaquin][:['Yes, the first option is the one that less surprises hold to the user.I also vote for 1. ]][^- The third week was over -][*Matias][:['There is still one problem that I have to solve. I need toknow if it is necessary to create a map_view associated to nothing. Ifit is necessary there are two options: that it behaves as an empty container orthat it throws an exception or assert when trying to use it. If it is not necessary,the map_view is going to keep a reference instead of a pointer.To me, the map_view always must be viewing something. In the case of the iteratorsbeing able to create them empty, makes them easy to use in contexts that requireconstructors by default, like being the value_type of a container, but I do notbelieve that this is the case of map_view.]][*Joaquin][:['How would an empty map_view be useful? My intuition is like yours,map_view would have to be always associate to something. If we wished to obtainthe semantics "is associated or not" we can use a pointer to a map_view. ]][*Matias][:['OK, then you agree to that map_views stores a reference insteadof a pointer?]][*Joaquin][:['It depends on the semantics you want to give to map_views, and inconcrete to the copy of map_views.]]``map_view x=...;map_view y=...;x=y;``[:['What is supposed to do this last line?]][:['1. Rebinding of x, that is to say, x points at the same container that y.]][:['2. Copy of the underlying container.]][:['If you want to implement 1, you cannot use references internally.If you want to implement 2, it is almost the same to use a reference or a pointer.]][*Matias][:['If I want that they behave exactly as std::maps then I must go for 2.But if I think they as "views" of something, I like 1. The question is complicated.I add another option:]][:['3. Error: operator= is declare as private in boost::bimap::map_view std_container]][:['Also What happens with `std_container = view;`? and with `view = std_container;`?]][endsect][endsect]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -