
One implementation detail of the date_time library is the constrained_value template class (constrained_value.hpp). I think this class is useful enough that it should be part of boost's public interface (maybe part of the utility library). After 1.35 is released, how difficult would it be to move this into the public interface? Joe Gottman

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Joe Gottman Sent: 08 November 2007 02:03 To: boost@lists.boost.org Subject: [boost] constrained_value.hpp
One implementation detail of the date_time library is the constrained_value template class (constrained_value.hpp). I think this class is useful enough that it should be part of boost's public interface (maybe part of the utility library).
I believe this would indeed be very widely useful to have a constrained type library. Robert Kawulak has also been working on this and on Aug 20 reported: "As for now, the implementation is complete, documentation is almost ready, tests and examples have to be finished. The code and documentation can be downloaded here: http://rk.go.pl/f/constrained_value.zip " This may have wider application than the date'n'time class? Perhaps we should encourage Robert to get his finished and in the Review queue? Paul --- Paul A Bristow Prizet Farmhouse, Kendal, Cumbria UK LA8 8AB +44 1539561830 & SMS, Mobile +44 7714 330204 & SMS pbristow@hetp.u-net.com

From: Paul A Bristow Perhaps we should encourage Robert to get his finished and in the Review queue?
Don't worry, I am planning to finish this. But looking at the (quite crowded) review queue I suppose there is no hurry... ;-) BTW, I'd like to ask if anybody has some comments, ideas or has encountered any problems with the existing code or documentation. Just to remind: The code and documentation can be downloaded here: http://rk.go.pl/f/constrained_value.zip The documentation is also available online here: http://rk.go.pl/r/constrained_value Todo: Mostly examples and tests. Known issues: bounded_error_handlers.hpp:86 should be: else if( c.is_above(nv) ) Best regards, Robert

Robert Kawulak wrote:
Don't worry, I am planning to finish this. But looking at the (quite crowded) review queue I suppose there is no hurry... ;-)
I think as it is now, if you can find yourself a review manager, you could effectively jump most of the queue. Just saying ... Sebastian

Sebastian Redl wrote:
Robert Kawulak wrote:
Don't worry, I am planning to finish this. But looking at the (quite crowded) review queue I suppose there is no hurry... ;-)
I think as it is now, if you can find yourself a review manager, you could effectively jump most of the queue.
Just saying ...
I'm glad this library is moving toward prime time. Looking over the docs, it looks pretty good -- pretty certain I could replace my date-time implementation with these. I have a few comments below. Anyway, I'm willing to be the review manager if that helps. I'm a little concerned with the dynamic changeability of the checking policy on bounded. I think that design decision will lead to function call overhead not present in the original implementation...even for bounded_int where the constraints can all be inline and fixed at compile time. I'd rather have 2 types if need be to allow maximum efficiency for the static cases. Couple thoughts on the docs. I didn't see Christopher Diggins acknowledged anywhere. He also took the date-time ideas and expanded them -- writing an article for C/C++ Users Journal. Here's some links: http://www.cdiggins.com/constrained_value.hpp http://www.artima.com/weblogs/viewpost.jsp?thread=79470 Second thing is that it would be nice to have tutorial style docs to walk someone thru use of writing a custom constraints (ok there's a simple one there), but also how to replace the provided exceptions with custom exceptions -- this isn't clear in the docs. I need to do custom exceptions in date-time -- the code there looks like: //! Exception type for gregorian year struct bad_year : public std::out_of_range { bad_year() : std::out_of_range(std::string("Year is out of valid range: 1400..10000")) {} }; //! Policy class that declares error handling gregorian year type typedef CV::simple_exception_policy<unsigned short, 1400, 10000, bad_year> greg_year_policies; Anyway the docs should describe how to do this. I also may have another contribution to this library in the future. I've written a constrained_string type which provides similar capabilities for strings. A typical use case is to only allow construction with a fixed set of strings -- basically like an enum: class image_string_policy : public string_set_policy<std::string, image_string_policy> { public: static set_type* init_set() { set_type* s = new set_type(); s->insert("png"); s->insert("jpg"); s->insert("jpeg"); s->insert("tiff"); return s; } }; //must construct with png, jpg, jpeg or tiff -- null is invalid typedef constrained_string<image_string_policy, std::string> image_string_type; //must construct with png, jpg, jpeg or tiff -- null ok typedef constrained_string_with_null<image_string_policy, std::string> image_string_type_with_null; image_string_type is("jpg"); //ok image_string_type is2("garbage"); //exception Another form allows fancy regex checking: class digit_string_policy : public regex_policy<std::string, digit_string_policy> { public: static const regex_type& get_regex() { static const regex_type pattern("\\d{4}"); return pattern; } }; typedef constrained_string<digit_string_policy, std::string> digit_string_type; digit_string_type ds("1234"); //ok digit_string_type ds2("12"); //exception-- four digits required Jeff

From: Jeff Garland Anyway, I'm willing to be the review manager if that helps.
That's great, thanks!
I'm a little concerned with the dynamic changeability of the checking policy on bounded. I think that design decision will lead to function call overhead not present in the original implementation...even for bounded_int where the constraints can all be inline and fixed at compile time. I'd rather have 2 types if need be to allow maximum efficiency for the static cases.
I wouldn't consider this a great issue. My experiments with compilation to assembly code show that compilers are able to optimise-away all (or at least much of) the abstraction easily (e.g. see http://article.gmane.org/gmane.comp.lib.boost.devel/164478). Moreover, I think that breaking the implementation into two cases based on efficiency reasons would break one of the basic Boost guidelines: "Aim first for clarity and correctness; optimization should be only a secondary concern in most Boost libraries."
Couple thoughts on the docs. I didn't see Christopher Diggins acknowledged anywhere.
This is what I was going to do. ;-)
Second thing is that it would be nice to have tutorial style docs to walk someone thru use of writing a custom constraints (ok there's a simple one there), but also how to replace the provided exceptions with custom exceptions
TBD
I've written a constrained_string type which provides similar capabilities for strings. A typical use case is to only allow construction with a fixed set of strings -- basically like an enum
Maybe it would be a good idea to write more general constraint policy, which allows for values of any type that belong to a specified set.
Another form allows fancy regex checking:
This one is nice too. ;-) Best regards, Robert

Robert Kawulak wrote:
constraints can all be inline and fixed at compile time. I'd rather have 2 types if need be to allow maximum efficiency for the static cases.
I wouldn't consider this a great issue. My experiments with compilation to assembly code show that compilers are able to optimise-away all (or at least much of) the abstraction easily (e.g. see http://article.gmane.org/gmane.comp.lib.boost.devel/164478).
Hmm, I'm dubious that this testcase is representative. Because you have a fixed const there is apparently no checking required...unlikely to work in most cases. Also, you still have the function call overhead...so I'm still leary that instead of 2 compares you get 2 compares plus the overhead in your example.
Moreover, I think that breaking the implementation into two cases based on efficiency reasons would break one of the basic Boost guidelines: "Aim first for clarity and correctness; optimization should be only a secondary concern in most Boost libraries."
Well, this type is an 'int replacement' so efficiency is a concern here. I guess I'm ok leaving it for now, but I'd like to see some deeper testing or the removal of the dynamic interface -- it can always be added later if someone makes a compelling use case for it. Personally, I don't think I have a need for it.
I've written a constrained_string type which provides similar capabilities for strings. A typical use case is to only allow construction with a fixed set of strings -- basically like an enum
Maybe it would be a good idea to write more general constraint policy, which allows for values of any type that belong to a specified set.
Good idea....it looks doable.
Another form allows fancy regex checking:
This one is nice too. ;-)
Thx :-) Jeff

From: Jeff Garland Robert Kawulak wrote:
constraints can all be inline and fixed at compile time. I'd rather have 2 types if need be to allow maximum efficiency for the static cases.
I wouldn't consider this a great issue. My experiments with compilation to assembly code show that compilers are able to optimise-away all (or at least much of) the abstraction easily (e.g. see http://article.gmane.org/gmane.comp.lib.boost.devel/164478).
Hmm, I'm dubious that this testcase is representative. Because you have a fixed const there is apparently no checking required...unlikely to work in most cases. Also, you still have the function call overhead...so I'm still leary that instead of 2 compares you get 2 compares plus the overhead in your example.
I'm not sure I understand - why you think there's a function call and 2 comparisons in that example? The function's code simply returns the literal value of 5 and that's it. Anyway, I've tried with more representative example - the value is not known at compile time: extern int i; void test() { bounded_int<int, 3, 5>::type b(i); } With #define NDEBUG and -O2 switch, gcc 4.0.1 gave the following code: __Z4testv: [...] irrelevant stack-manipulation code movl _i, %eax movl %eax, -32(%ebp) cmpl $2, %eax <== lower bound jle L8 cmpl $5, %eax <== upper bound jle L46 L8: [...] irrelevant error-handling code L46: [...] irrelevant stack-manipulation code ret With the same options, Borland C++ 5.6.4 gave: @@test$qv proc near [...] lots of seemingly irrelevant code mov eax,3 <== lower bound [...] cmp eax,edx setg al and eax,1 test al,al jne short @19 mov eax,5 <== upper bound cmp edx,eax setg cl and ecx,1 test cl,cl je @17 @19: [...] irrelevant error-handling code @17: [...] ret @@test$qv endp In both cases the compilers were able to optimise-away the abstraction of bounds generators, using the literal values of bounds in the comparisons. I'm not sure this is what you were worrying about, but if so, then I think there's no problem at all. ;-) Best regards, Robert

Robert Kawulak wrote:
From: Jeff Garland
I'm not sure I understand - why you think there's a function call and 2 comparisons in that example?
The function's code simply returns the literal value of 5 and that's it.
Anyway, I've tried with more representative example - the value is not known at compile time:
extern int i;
void test() { bounded_int<int, 3, 5>::type b(i); }
With #define NDEBUG and -O2 switch, gcc 4.0.1 gave the following code:
__Z4testv: [...] irrelevant stack-manipulation code
That 'irrelevant stack manipulation code' isn't irrelevant at all...it's the function call overhead cost and would likely *not exist* in a fully static version. Same with the stuff right before the return. <snip>...good details...
In both cases the compilers were able to optimise-away the abstraction of bounds generators, using the literal values of bounds in the comparisons. I'm not sure this is what you were worrying about, but if so, then I think there's no problem at all. ;-)
Nope...it's the stack code which can't be optimized out because you have to have a function pointer and call a different location instead of fully inlining. If you take out the dynamic function part I bet you'll cut the generated code down to just the compares and the error handling... Jeff

On Nov 13, 2007 3:19 AM, Jeff Garland <jeff@crystalclearsoftware.com> wrote:
Robert Kawulak wrote:
From: Jeff Garland
I'm not sure I understand - why you think there's a function call and 2 comparisons in that example?
The function's code simply returns the literal value of 5 and that's it.
Anyway, I've tried with more representative example - the value is not known at compile time:
extern int i;
void test() { bounded_int<int, 3, 5>::type b(i); }
With #define NDEBUG and -O2 switch, gcc 4.0.1 gave the following code:
__Z4testv: [...] irrelevant stack-manipulation code
That 'irrelevant stack manipulation code' isn't irrelevant at all...it's the function call overhead cost and would likely *not exist* in a fully static version. Same with the stuff right before the return.
Actually the stack manipulation is for the test function, not for the bound checking function, so, as Robert said, it is completely irrelevant in this context. In fact the __Z4testv: is just that, the mangled name of the test function.
<snip>...good details...
In both cases the compilers were able to optimise-away the abstraction of bounds generators, using the literal values of bounds in the comparisons. I'm not sure this is what you were worrying about, but if so, then I think there's no problem at all. ;-)
Nope...it's the stack code which can't be optimized out because you have to have a function pointer and call a different location instead of fully inlining. If you take out the dynamic function part I bet you'll cut the generated code down to just the compares and the error handling...
I was surprised for the lack of call to bound functions, because in my experience gcc is completely incapable to inline function pointers (except when used as non type template parameters), so I went and looked at the code.
From a cursory look I can se no use of function pointers, nor I would expect it: the bounds are knonw at compile time so there is no need of dynamic dispatch. The framework seems to allow dynamic (runtime) bounds, but even in that case, i see no need for dynamic dispatch.
BTW, when compiling with gcc 4.1.2 under x86_64: .globl my_test_function() .type my_test_function(), @function my_test_function(): .LFB1638: [ stack frame setup for test function ] movq %rbx, -32(%rsp) .LCFI6: movq %rbp, -24(%rsp) .LCFI7: movq %r12, -16(%rsp) .LCFI8: movq %r13, -8(%rsp) .LCFI9: subq $72, %rsp .LCFI10: [ load i and bound check it ] movl i(%rip), %eax cmpl $2, %eax jle .L44 cmpl $5, %eax jle .L71 .L44: [ error detection ... snipped ] .L71: [ stack frame tear down ] movq 40(%rsp), %rbx movq 48(%rsp), %rbp movq 56(%rsp), %r12 movq 64(%rsp), %r13 addq $72, %rsp ret This means that the bound checking code is just 4 instructions. I think you can hardly do better than that. This is even better than the disassembly reported by Robert because it skips bound loads in %eax and directly encodes immediates as an operand of the cmp instruction. This saves two instruction, but I'm not sure if in practice it makes a difference. BTW, to be 100% honest, the stack frames setup up and tear down are not completely unrelated to the usage of the bounded integer. Even if the fast path only partically uses one register (rax, which is callee clobbered and doesn't need saving), four more registers are saved even if they are only used in the slow error checking path. If the compiler could figure that the error checking path is not entered often, it could move the save and restore in that path. Anyways, in a real life function you would need to use many registers anyway, so this doesn't really matter. And yes, I'm interested in a bounded integers library. HTH, -- gpd

Giovanni Piero Deretta wrote:
On Nov 13, 2007 3:19 AM, Jeff Garland <jeff@crystalclearsoftware.com> wrote:
That 'irrelevant stack manipulation code' isn't irrelevant at all...it's the function call overhead cost and would likely *not exist* in a fully static version. Same with the stuff right before the return.
Actually the stack manipulation is for the test function, not for the bound checking function, so, as Robert said, it is completely irrelevant in this context. In fact the __Z4testv: is just that, the mangled name of the test function.
<snip>...
From a cursory look I can se no use of function pointers, nor I would expect it: the bounds are knonw at compile time so there is no need of dynamic dispatch. The framework seems to allow dynamic (runtime) bounds, but even in that case, i see no need for dynamic dispatch.
Ok, fair enough. I must have some misunderstanding of the implementation b/c I don't see how the function call can be optimized away by the compiler when the function pointer can be set at runtime. Anyway, I'm satisfied that it's efficient as possible. Jeff

From: Jeff Garland Ok, fair enough. I must have some misunderstanding of the implementation b/c I don't see how the function call can be optimized away by the compiler when the function pointer can be set at runtime.
That's simple - there are no function pointers in this case. ;-) The design is flexible and allows for dynamically-changed constraints, but it also allows for completely static constraints, the latter being the case here. A constraint is a predicate - that's all. You can use boost::function to be able to dynamically change the constraint, or you can use a predicate directly as the template's parameter to make it statically associated with the type of used constrained objects. Best regards, Robert

Hi, I apologise for the delay with the library. I was expecting to find some time to prepare it for formal review submission in November, but unfortunatelly I didn't. Anyway, I'm still willing to make the submission as soon as possible. I just want to add at least examples and tutorial to the docs to make the usage and design of the library clear without digging into the code. Best regards, Robert Kawulak -=##############=- <gg> 6934572 </gg> <gsm> +48603654215 </gsm> <www> rk.go.pl </www> -=##############=- "I know not with what weapons World War III will be fought, but World War IV will be fought with sticks and stones." - Albert Einstein
participants (6)
-
Giovanni Piero Deretta
-
Jeff Garland
-
Joe Gottman
-
Paul A Bristow
-
Robert Kawulak
-
Sebastian Redl