C++11 Feature Proposal: decltype

759 views
Skip to first unread message

Peter Kasting

unread,
Sep 24, 2014, 7:22:12 PM9/24/14
to Chromium-dev
What: decltype(expr) determines the type of expr at compile time.
Why: This is mostly useful in template code.  We have a limited amount of templates in Chromium, but there are some, and especially in cases that need to declare variables of the same type as some of the template args, decltype can help make the code more readable, e.g. by eliminating the need to pass the type as a template arg.
Why not: decltype() used in a place where the actual type is obvious is basically a longer, less-readable form of "auto"

Detail: I propose that decltype() be allowed in general.  I don't think there's much temptation to use it when it's not necessary, so I don't think we have to worry about it cluttering and harming code.  Having done some work on template functions recently, decltype() would have been a nice way to make the code a little cleaner and be able to remember that "this expression's type matches arg A" instead of trying to pass types as <T, U, V> and remember which of those letters corresponds to what.

PK

Alex Vakulenko

unread,
Sep 24, 2014, 7:32:04 PM9/24/14
to Peter Kasting, Chromium-dev
In Chrome OS we have switched most components to C++11 features long time ago, but we had to maintain GCC language extensions (using --std=gnu++11 instead of standard --std==c++11) simply because of one thing that comes from Chrome:


If decltype is allowed in Chrome code, not only will you make this nice language feature to be used by Chrome team, you will also save Chrome OS code base from non-standard language extension atrocities :)



--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev

James Robinson

unread,
Sep 25, 2014, 2:08:49 AM9/25/14
to Peter Kasting, Chromium-dev
On Wed, Sep 24, 2014 at 4:21 PM, 'Peter Kasting' via Chromium-dev <chromi...@chromium.org> wrote:
Could you link to some places where you'd like to use this feature in chromium?  I'm inclined to say it should be allowed for cases where it's really needed but be subject to careful review, at least at first, since it should be pretty rare and used only in subtle code that could probably stand a closer look.

- James

Peter Kasting

unread,
Sep 25, 2014, 4:19:46 AM9/25/14
to James Robinson, Chromium-dev
The changes of mine you just reviewed to clampTo() could possibly be written slightly more simply and clearly using decltype().  I also had some changes to fix some math functions in I-forget-which library (sorry!) where I had to hack around not having decltype by making all callers pass in the type they wanted to cast things to.  I would link to the latter if I could recall where it was :/.  I just remember thinking as I wrote it, "man, this would be way better if I had decltype".

I have no objection to something like "if you're using decltype, get really good review."

PK

Victor Khimenko

unread,
Sep 25, 2014, 5:03:25 AM9/25/14
to Peter Kasting, James Robinson, Chromium-dev
That's kind of too vague. What about "Use decltype only in places where you don't know the actual type of an object and where you'll need to do a redesign our CL to avoid use of decltype. Avoid it if it's possible to replace it with concrete type or the argument of template."

This will automatically limit it to templates (if that't not a template then type is always known) and then only to templates which do "something unusual" - and these will probably attract attention of reviewer anyway. 

Peter Kasting

unread,
Sep 25, 2014, 5:15:10 AM9/25/14
to Victor Khimenko, James Robinson, Chromium-dev
On Thu, Sep 25, 2014 at 2:02 AM, Victor Khimenko <kh...@chromium.org> wrote:
That's kind of too vague. What about "Use decltype only in places where you don't know the actual type of an object and where you'll need to do a redesign our CL to avoid use of decltype. Avoid it if it's possible to replace it with concrete type or the argument of template."

This will automatically limit it to templates (if that't not a template then type is always known) and then only to templates which do "something unusual" - and these will probably attract attention of reviewer anyway.

No, I explicitly don't want that level of strictness.  It's often possible to avoid decltype by passing more type arguments to the template, or by using template type arguments in more places within the template implementation.  The point of decltype is not just to make possible the exceptional cases where that's not true, it's to make other cases clearer and more readable.  Sometimes

decltype(x) y = ...;

is clearer than

T y = ...;

...when it makes it apparent to the reader that "y has to match the type of x" and it's perhaps not instantly clear (maybe because your template takes multiple type arguments) what the type of x is, or your template didn't otherwise need to take T as a param.

PK

Victor Khimenko

unread,
Sep 25, 2014, 5:37:13 AM9/25/14
to Peter Kasting, James Robinson, Chromium-dev
On Thu, Sep 25, 2014 at 1:14 PM, Peter Kasting <pkas...@google.com> wrote:
On Thu, Sep 25, 2014 at 2:02 AM, Victor Khimenko <kh...@chromium.org> wrote:
That's kind of too vague. What about "Use decltype only in places where you don't know the actual type of an object and where you'll need to do a redesign our CL to avoid use of decltype. Avoid it if it's possible to replace it with concrete type or the argument of template."

This will automatically limit it to templates (if that't not a template then type is always known) and then only to templates which do "something unusual" - and these will probably attract attention of reviewer anyway.

No, I explicitly don't want that level of strictness.  It's often possible to avoid decltype by passing more type arguments to the template,

That approach is obviously a "you'll need to redesign a CL" case which I'm talking about. If you could remove decltype by doing local changes IN PLACE WHERE decltype is written WITHOUT changes in other places then it must be done, if you'll need to change users of your API, change arguments of template, etc then that's non-local change and in such case decltype is allowed.

or by using template type arguments in more places within the template implementation.

Is it really such a big problem?
 
  The point of decltype is not just to make possible the exceptional cases where that's not true, it's to make other cases clearer and more readable.  Sometimes

decltype(x) y = ...;

is clearer than

T y = ...;

...when it makes it apparent to the reader that "y has to match the type of x" and it's perhaps not instantly clear (maybe because your template takes multiple type arguments) what the type of x is, or your template didn't otherwise need to take T as a param.

Could you show me a CL where it'll be a clear win? I can understand the situation where decltype is used to match the result of something created "a long time ago in a galaxy far, far away", but when you could just use an existing parameter of template... is it still a win worth discussing? "if you template don't otherwise need to take T as a param" is covered by above proposal. We could even add a note that if you could completely remove argument of a template if you'll use decltype then it's recommended to do that.

Basically my POV is: decltype is a kludge and is hard to understand. But if use of it makes life easier in OTHER places (less arguments to templates, no need to redesign a large component, etc) then it's usually a net win. Less parametes in templates are always good if that does not limit expressiveness.

But when you could just place something else in place of decltype without changes in other places then it's rarely a net win and even if it's a net win it's a minor one at best.

We could always relax the requirements in the future.

Stuart Morgan

unread,
Sep 25, 2014, 9:47:49 AM9/25/14
to kh...@chromium.org, Peter Kasting, James Robinson, Chromium-dev
On Thu, Sep 25, 2014 at 2:36 AM, Victor Khimenko <kh...@chromium.org> wrote:
Basically my POV is: decltype is a kludge and is hard to understand. But if use of it makes life easier in OTHER places (less arguments to templates, no need to redesign a large component, etc) then it's usually a net win. Less parametes in templates are always good if that does not limit expressiveness.

But when you could just place something else in place of decltype without changes in other places then it's rarely a net win and even if it's a net win it's a minor one at best.

Is this more true in Chromium than in Google code? Are Chromium engineers less capable than Google engineers of using it only when appropriate, or of pointing out 'bad' usage in review? If not one of these, why does Chromium need to deviate from Google's style guide here?

-Stuart

Victor Khimenko

unread,
Sep 25, 2014, 10:11:31 AM9/25/14
to Stuart Morgan, Peter Kasting, James Robinson, Chromium-dev
Why are we having these discussions at all? Why not allow the same C++11 features which are allowed in Google's style guide?

The reasoning WRT auto and decltype is the same as with any other C++11 feature, but it's especially important for abuse-prone features like "auto" and "decltype": people must first learn to use them where impact is clearly positive then we could expand allowed scope and at some point restrictions could be removed altogether.

Two years ago, when C++11 was first allowed in Google code, style guide contained pretty short list of whitelisted features. Later they have switched to blacklist and today that blacklist is pretty short. I guess Chromium is destined to repeat this path although we don't necessarily must repeat it exactly.

Colin Blundell

unread,
Sep 25, 2014, 10:18:35 AM9/25/14
to kh...@chromium.org, Stuart Morgan, Peter Kasting, James Robinson, Chromium-dev
On Thu, Sep 25, 2014 at 4:10 PM, Victor Khimenko <kh...@chromium.org> wrote:

On Thu, Sep 25, 2014 at 5:47 PM, Stuart Morgan <stuart...@chromium.org> wrote:
On Thu, Sep 25, 2014 at 2:36 AM, Victor Khimenko <kh...@chromium.org> wrote:
Basically my POV is: decltype is a kludge and is hard to understand. But if use of it makes life easier in OTHER places (less arguments to templates, no need to redesign a large component, etc) then it's usually a net win. Less parametes in templates are always good if that does not limit expressiveness.

But when you could just place something else in place of decltype without changes in other places then it's rarely a net win and even if it's a net win it's a minor one at best.

Is this more true in Chromium than in Google code? Are Chromium engineers less capable than Google engineers of using it only when appropriate, or of pointing out 'bad' usage in review? If not one of these, why does Chromium need to deviate from Google's style guide here?

Why are we having these discussions at all? Why not allow the same C++11 features which are allowed in Google's style guide?


Note that Stuart and Peter have explicitly argued that this should be Chromium's position on the thread introducing C++11 support in Chromium (with the exception of differences that are mandated due to Chromium lacking library support at this time). Personally, I agree with them.
  
The reasoning WRT auto and decltype is the same as with any other C++11 feature, but it's especially important for abuse-prone features like "auto" and "decltype": people must first learn to use them where impact is clearly positive then we could expand allowed scope and at some point restrictions could be removed altogether.

Two years ago, when C++11 was first allowed in Google code, style guide contained pretty short list of whitelisted features. Later they have switched to blacklist and today that blacklist is pretty short. I guess Chromium is destined to repeat this path although we don't necessarily must repeat it exactly.

Victor Khimenko

unread,
Sep 25, 2014, 10:30:35 AM9/25/14
to avaku...@chromium.org, Peter Kasting, Chromium-dev
On Thu, Sep 25, 2014 at 3:31 AM, Alex Vakulenko <avaku...@chromium.org> wrote:
In Chrome OS we have switched most components to C++11 features long time ago, but we had to maintain GCC language extensions (using --std=gnu++11 instead of standard --std==c++11) simply because of one thing that comes from Chrome:


Perhaps this could should just be fixed instead?
 
If decltype is allowed in Chrome code, not only will you make this nice language feature to be used by Chrome team, you will also save Chrome OS code base from non-standard language extension atrocities :)

That's separate issue. "Non-standard language extension atrocities" could easily be used in standard compatible way. Compare:


$ cat test1.cc
int foo(int);
int bar(int x) {
  return ({
      typeof(x) result;
      do {
        result = foo(x);
      } while (result != -1);
    result; });
}
$ g++ -pedantic -c -std=c++11 test1.cc 
test1.cc: In function 'int bar(int)':
test1.cc:4:10: warning: ISO C++ forbids braced-groups within expressions [-Wpedantic]
   return ({
          ^
test1.cc:5:15: error: 'typeof' was not declared in this scope
       typeof(x) result;
               ^
test1.cc:7:9: error: 'result' was not declared in this scope
         result = foo(x);
         ^
test1.cc:8:16: error: 'result' was not declared in this scope
       } while (result != -1);
                ^

Uh-oh... looks like we REALLY need gnu++11 mode! Well... not exactly:

$ cat test2.cc
int foo(int);
int bar(int x) {
  return __extension__ ({
      __typeof__(x) result;
      do {
        result = foo(x);
      } while (result != -1);
    result; });
}
$ g++ -pedantic -c -std=c++98 test2.cc

It's __extension__ thus it's allowed! Note that you must use __typeof__ instead of typeof here because in pedantic mode non-standard keywords without underscores are never supported - even with __extension__ mark.

Stuart Morgan

unread,
Sep 25, 2014, 10:37:18 AM9/25/14
to Colin Blundell, kh...@chromium.org, Peter Kasting, James Robinson, Chromium-dev
On Thu, Sep 25, 2014 at 7:17 AM, Colin Blundell <blun...@chromium.org> wrote:
Note that Stuart and Peter have explicitly argued that this should be Chromium's position on the thread introducing C++11 support in Chromium (with the exception of differences that are mandated due to Chromium lacking library support at this time).

Clarifying my position a bit: I think there could be non-library-support reasons for Chromium to do different things in some cases, so having discussions seems worthwhile. I just don't think those discussions should be completely green-field, and should instead follow the pattern we use for basically everything else in the Chromium style guide: arguing only why (if at all) Chromium style should be different than Google style because of something specific to Chromium.

I was repeating that argument here because this thread seems like a perfect example of what I don't think we should be doing, in that as far as I can tell no objections raised here have been specific to Chromium in any way.

-Stuart

Alex Vakulenko

unread,
Sep 25, 2014, 11:10:05 AM9/25/14
to Victor Khimenko, Peter Kasting, Chromium-dev
I was actually thinking of fixing HANDLE_EINTR by converting it into a template function instead it being a macro down the road. The current (less obvious) problem with this macro is that it re-evaluates the function call arguments on each retry and if you do something like "int a = HANDLE_EINTR(open(files[index++], mode));" that's going to blow up if open returns EINTR error and the operation is retried. Here is the current definition of that macro:
#define HANDLE_EINTR(x) ({ \
  int eintr_wrapper_counter = 0; \
  typeof(x) eintr_wrapper_result; \
  do { \
    eintr_wrapper_result = (x); \
  } while (eintr_wrapper_result == -1 && errno == EINTR && \
           eintr_wrapper_counter++ < 100); \
  eintr_wrapper_result; \
})
I was thinking of eventually having something like this instead:
template<typename Function, typename... Args>
inline auto HANDLE_EINTR(Function fnc, Args... args) -> decltype(fnc(args...)) {
  using RetType = decltype(fnc(args...));
  static_assert(helper::IsSignedInteger<RetType>::value,
                "Function must return a signed integer value");
  RetType retval{};
  int counter = 0;
  do {
    retval = fnc(args...);
  } while (retval < 0 && errno == EINTR && counter++ < 100);
  return retval;
}

(do I get a prize for using the most new C++11 features in one function? :))

So, HANDLE_EINTR is a function that takes a delegate 'fnc' (which will most likely be a function pointer) and its arguments, and returns the same type as the delegate (while still checking that it is a signed integer along the way, which should have used std::is_signed<> but barring the library support it shouldn't be too difficult to create a custom implementation).

The semantics of this is a bit different, so instead of "val = HANDLE_EINTR(open(file, mode));" you'd use it like "val = HANDLE_EINTR(open, file, mode);" but I think it's a reasonable trade-off given the fact that the arguments will be evaluated only once no matter how many times the function call is retried...

Anyway, I suspect this implementation is far away since it uses a bunch of features that are still to be approved.

And to definitely get the prize, I will add perfect forwarding for the arguments, so we eliminate extra copies of strings and what not:
template<typename Function, typename... Args>
inline auto HANDLE_EINTR(Function fnc, Args&&... args) -> decltype(fnc(std::forward<Args>(args)...)) {
  using RetType = decltype(fnc(std::forward<Args>(args)...));
  static_assert(helper::IsSignedInteger<RetType>::value,
                "Function must return a signed integer value");
  RetType retval{};
  int counter = 0;
  do {
    retval = fnc(std::forward<Args>(args)...);
  } while (retval < 0 && errno == EINTR && counter++ < 100);
  return retval;
}
Now, that's a keeper :)

Markus Gutschke (顧孟勤)

unread,
Sep 25, 2014, 4:07:13 PM9/25/14
to kh...@chromium.org, avaku...@chromium.org, Peter Kasting, Chromium-dev
In an ideal world, we wouldn't worry about the implementation of HANDLE_EINTR() at all. This should instead be done by the system library. If compiling _GNU_SOURCE, <unistd.h> defines TEMP_FAILURE_RETRY(). This is the standard way to implement HANDLE_EINTR().

If you read the header file, you'll see that it avoids using "typeof" or "decltype", as it presumably has enough knowledge about the platform to decide that "long int" works for all functions that can meaningfully use this macro. This surprises me a little bit, but presumably the glibc library authors know what they are doing.

Unfortunately, being a GNU extension, I suspect we can't use TEMP_FAILURE_RETRY().


Markus


On Thu, Sep 25, 2014 at 7:29 AM, Victor Khimenko <kh...@chromium.org> wrote:

Justin Schuh

unread,
Oct 13, 2014, 9:51:46 AM10/13/14
to mar...@chromium.org, kh...@chromium.org, avaku...@chromium.org, Peter Kasting, Chromium-dev
Jumping in a bit late, but I had to write some ugly template hackery in base/numerics because I couldn't use decltype. As an example, the ArithmeticPromotion template in base/numerics/safe_math_impl.h is a reimplementation of the C/C++ integer promotion rules:

template <typename Lhs,
          typename Rhs = Lhs,
          ArithmeticPromotionCategory Promotion =
              (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
                  ? (MaxExponent<Lhs>::value > MaxExponent<int>::value
                         ? LEFT_PROMOTION
                         : DEFAULT_PROMOTION)
                  : (MaxExponent<Rhs>::value > MaxExponent<int>::value
                         ? RIGHT_PROMOTION
                         : DEFAULT_PROMOTION) >
struct ArithmeticPromotion;

template <typename Lhs, typename Rhs>
struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
  typedef Lhs type;
};

template <typename Lhs, typename Rhs>
struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
  typedef Rhs type;
};

template <typename Lhs, typename Rhs>
struct ArithmeticPromotion<Lhs, Rhs, DEFAULT_PROMOTION> {
  typedef int type;
};

The much cleaner and simpler way to do this is to just use a decltype expression in place of the template, and let the compiler work out the correct promotions. And the bonus is that decltype won't require additional logic if e.g. I need to add support 128-bit integers in the future.


Nico Weber

unread,
Oct 13, 2014, 11:20:14 AM10/13/14
to Justin Schuh, Markus Gutschke, Victor Khimenko, avaku...@chromium.org, Peter Kasting, Chromium-dev
It looks like there's a few places where this is useful, and it won't be used in most places. So I think just allowing this with a "usage should be rare" comment (like e.g. variadic templates) is fine. Justin, want to send a CL for this?

(It's not obvious to me how this'll help in Justin's case, but getting rid of our typeofs so we can switch from -std=gnu++11 to -std=c++11 seems like motivation enough.)

Nico Weber

unread,
Oct 28, 2014, 12:42:42 AM10/28/14
to Justin Schuh, Markus Gutschke, Victor Khimenko, avaku...@chromium.org, Peter Kasting, Chromium-dev
This is now allowed: http://chromium-cpp.appspot.com/
Reply all
Reply to author
Forward
0 new messages