Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #6082 > unrolled thread

Casting template argument

Started byMarcel Müller <news.5.maazl@spamgourmet.org>
First post2011-06-02 14:33 +0200
Last post2011-06-02 10:40 -1000
Articles 6 — 3 participants

Back to article view | Back to comp.lang.c++


Contents

  Casting template argument Marcel Müller <news.5.maazl@spamgourmet.org> - 2011-06-02 14:33 +0200
    Re: Casting template argument Victor Bazarov <v.bazarov@comcast.invalid> - 2011-06-02 10:17 -0400
      Re: Casting template argument Marcel Müller <news.5.maazl@spamgourmet.org> - 2011-06-02 22:10 +0200
        Re: Casting template argument Pete Becker <pete@versatilecoding.com> - 2011-06-02 10:21 -1000
          Re: Casting template argument Marcel Müller <news.5.maazl@spamgourmet.org> - 2011-06-02 22:34 +0200
            Re: Casting template argument Pete Becker <pete@versatilecoding.com> - 2011-06-02 10:40 -1000

#6082 — Casting template argument

FromMarcel Müller <news.5.maazl@spamgourmet.org>
Date2011-06-02 14:33 +0200
SubjectCasting template argument
Message-ID<4de78326$0$6554$9b4e6d93@newsspool4.arcor-online.net>
I want to cast a template argument. Unfortunately gcc creates an error 
as if the cast has never been there. Is this not allowed generally?


-----
#include <string.h>

// Sorted vector of T with the key type K using the comparer C.
template <class T, class K, int (*C)(const T* e, const K* k)>
class sorted_vector
{public:
   // Find an element by it's key.
   // The function will return NULL if no such element is in the container.
   T*                 find(const K* key) const;
   // Ensure an element with a particular key.
   // This will either return a reference to a pointer to an existing object
   // which equals to key or a reference to a NULL pointer which is
   // created at the location in the container where a new object with key
   // should be inserted.
   T*&                get(const K* key);
   // Erase the element which equals key and return the removed pointer.
   // If no such element exists the function returns NULL.
   T*                 erase(const K* key);
};

// data structure, POD
struct data
{ char Key[10];
   char Value[10];
};

// Works
int (*const comparer)(const data*, const char*)
   = (int (*)(const data*, const char*))&strcmp;

// does not work, see below
typedef sorted_vector<data, char,
   (int (*)(const data*, const char*))&strcmp> stringmap;
-----

test6.cpp:34: error: could not convert template argument `strcmp' to `int
    (*)(const data*, const char*)'


Of cource, strcmp does not compare against objects of type data, but 
since data is a POD type the equivalence of const data* and const char* 
should not raise serious problems, and a helper function should be 
superfluous.


Marcel

[toc] | [next] | [standalone]


#6083

FromVictor Bazarov <v.bazarov@comcast.invalid>
Date2011-06-02 10:17 -0400
Message-ID<is8629$ook$1@dont-email.me>
In reply to#6082
On 6/2/2011 8:33 AM, Marcel Müller wrote:
> I want to cast a template argument. Unfortunately gcc creates an error
> as if the cast has never been there. Is this not allowed generally?

Yep.  Not allowed.

The requirement is that a non-type template argument is expected to be 
the address of a function, "expressed as & id-expression where the & is 
optional if the name refers to a function or array".  That means that 
the "explicit type conversion expressions" are *not allowed*.  Bummer.

What you perhaps need is to supply your pointer expression (by just 
using the name of the function) and hope that the compiler performs the 
necessary conversion itself.  Standard, [temp.arg.nontype]/5 explains 
what conversions are applied:
<<
The following conversions are performed on each expression used as a 
non-type template-argument. If a non-type template-argument cannot be 
converted to the type of the corresponding template-parameter then
the program is ill-formed.
— for a non-type template-parameter of integral or enumeration type, 
integral promotions (4.5) and integral conversions (4.7) are applied.
— for a non-type template-parameter of type pointer to object, 
qualification conversions (4.4) and the array-to-pointer conversion 
(4.2) are applied. [Note: In particular, neither the null pointer 
conversion (4.10) nor the derived-to-base conversion (4.10) are applied. 
Although 0 is a valid template-argument for a non-type 
template-parameter of integral type, it is not a valid template-argument 
for a non-type template-parameter of pointer type. ]
— For a non-type template-parameter of type reference to object, no 
conversions apply. The type referred to by the reference may be more 
cv-qualified than the (otherwise identical) type of the 
templateargument. The template-parameter is bound directly to the 
template-argument, which must be an lvalue.
— For a non-type template-parameter of type pointer to function, only 
the function-to-pointer conversion (4.3) is applied. If the 
template-argument represents a set of overloaded functions (or a pointer 
to such), the matching function is selected from the set (13.4).
— For a non-type template-parameter of type reference to function, no 
conversions apply. If the template-argument represents a set of 
overloaded functions, the matching function is selected from the
set (13.4).
— For a non-type template-parameter of type pointer to member function, 
no conversions apply. If the template-argument represents a set of 
overloaded member functions, the matching member function is
selected from the set (13.4).
— For a non-type template-parameter of type pointer to data member, 
qualification conversions (4.4) are applied.>>

Your case seems to fall into the fourth bullet point.  What you will 
likely to encounter is another compiler error since the function is not 
of the type expected by the compiler.  You need a wrapper function.

> -----
> #include <string.h>
>
> // Sorted vector of T with the key type K using the comparer C.
> template <class T, class K, int (*C)(const T* e, const K* k)>
> class sorted_vector
> {public:
> // Find an element by it's key.
> // The function will return NULL if no such element is in the container.
> T* find(const K* key) const;
> // Ensure an element with a particular key.
> // This will either return a reference to a pointer to an existing object
> // which equals to key or a reference to a NULL pointer which is
> // created at the location in the container where a new object with key
> // should be inserted.
> T*& get(const K* key);
> // Erase the element which equals key and return the removed pointer.
> // If no such element exists the function returns NULL.
> T* erase(const K* key);
> };
>
> // data structure, POD
> struct data
> { char Key[10];
> char Value[10];
> };
>
> // Works
> int (*const comparer)(const data*, const char*)
> = (int (*)(const data*, const char*))&strcmp;

"Works" - what do you mean?  You have a C-style cast here.  Whether it 
achieves what you expect is not known until you actually try invoking 
that function using that 'comparer' pointer.

> // does not work, see below
> typedef sorted_vector<data, char,
> (int (*)(const data*, const char*))&strcmp> stringmap;

Have you tried without the cast?  What do you get?

> -----
>
> test6.cpp:34: error: could not convert template argument `strcmp' to `int
> (*)(const data*, const char*)'
>
>
> Of cource, strcmp does not compare against objects of type data, but
> since data is a POD type the equivalence of const data* and const char*
> should not raise serious problems, and a helper function should be
> superfluous.

<shrug>  You're trying to cheat the system by using the C-style cast. 
That's *usually* an invitation for UB.

V
-- 
I do not respond to top-posted replies, please don't ask

[toc] | [prev] | [next] | [standalone]


#6091

FromMarcel Müller <news.5.maazl@spamgourmet.org>
Date2011-06-02 22:10 +0200
Message-ID<4de7ee37$0$7626$9b4e6d93@newsspool1.arcor-online.net>
In reply to#6083
On 02.06.11 16.17, Victor Bazarov wrote:
> On 6/2/2011 8:33 AM, Marcel Müller wrote:
>> I want to cast a template argument. Unfortunately gcc creates an error
>> as if the cast has never been there. Is this not allowed generally?
>
> Yep. Not allowed.
[...]
> Your case seems to fall into the fourth bullet point. What you will
> likely to encounter is another compiler error since the function is not
> of the type expected by the compiler. You need a wrapper function.

What a pity.
Unfortunately I need a dozen wrappers, because I have different data 
types that differ only in the value type. This can be easily coded by a 
template function but it is not that pretty.

>> // Works
>> int (*const comparer)(const data*, const char*)
>> = (int (*)(const data*, const char*))&strcmp;
>
> "Works" - what do you mean? You have a C-style cast here. Whether it
> achieves what you expect is not known until you actually try invoking
> that function using that 'comparer' pointer.

Following the Rules of POD types, the pointer to the structure and to 
the first structure element should be the same. And as far as the 
strings are null terminated i see no problem.
In fact I showed not the excerpt of my code, but a reduced one for 
demonstration purpose only. The real implementation of sorted_vector<> 
internally works only with void* to prevent code size explosions. The 
function is cast to int (*)(const void*, const void*). The core worker 
knows nothing about the real data type. sorted_vector is a type safe 
wrapper. This has been working for many years.


>> // does not work, see below
>> typedef sorted_vector<data, char,
>> (int (*)(const data*, const char*))&strcmp> stringmap;
>
> Have you tried without the cast? What do you get?

Exactly the same message. In this case I did not expect anything else.


>> Of cource, strcmp does not compare against objects of type data, but
>> since data is a POD type the equivalence of const data* and const char*
>> should not raise serious problems, and a helper function should be
>> superfluous.
>
> <shrug> You're trying to cheat the system by using the C-style cast.
> That's *usually* an invitation for UB.

Well, the application is platform dependent for other, functional 
reasons anyway. The reinterpret cast from const data* to const char* is 
AFAIK no UB. But this does not automatically apply to casting the 
function pointer to a function with another argument type. On my target 
platform it makes no difference. All pointers arguments except for 
member function pointers are treated in the same way. So what is UB in 
general is not necessarily UB on this specific platform.


Marcel

[toc] | [prev] | [next] | [standalone]


#6093

FromPete Becker <pete@versatilecoding.com>
Date2011-06-02 10:21 -1000
Message-ID<2011060210211182589-pete@versatilecodingcom>
In reply to#6091
On 2011-06-02 10:10:30 -1000, Marcel Müller said:

> 
> Well, the application is platform dependent for other, functional 
> reasons anyway. The reinterpret cast from const data* to const char* is 
> AFAIK no UB. But this does not automatically apply to casting the 
> function pointer to a function with another argument type. On my target 
> platform it makes no difference. All pointers arguments except for 
> member function pointers are treated in the same way. So what is UB in 
> general is not necessarily UB on this specific platform.
> 

Undefined behavior arises from valid code constructs for which the C++ 
language definition does not impose any requirements. That doesn't 
depend in any way on the compiler that you're using or on the target 
platform.

The *consequences* of using code whose behavior is undefined depend on 
the compiler and the platform.

-- 
  Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The 
Standard C++ Library Extensions: a Tutorial and Reference 
(www.petebecker.com/tr1book)

[toc] | [prev] | [next] | [standalone]


#6097

FromMarcel Müller <news.5.maazl@spamgourmet.org>
Date2011-06-02 22:34 +0200
Message-ID<4de7f3f0$0$7624$9b4e6d93@newsspool1.arcor-online.net>
In reply to#6093
On 02.06.11 22.21, Pete Becker wrote:
>> So what is
>> UB in general is not necessarily UB on this specific platform.
>
> Undefined behavior arises from valid code constructs for which the C++
> language definition does not impose any requirements. That doesn't
> depend in any way on the compiler that you're using or on the target
> platform.

This is UB with respect to the C++ language. The documentation of a 
specific compiler may define the behavior in cases where the C++ 
language does not.
Passing pointer arguments is exactly defined on my platform. So the 
result of my cast is defined behavior, by all means with the three 
compilers that are supported (IBM, gnu, Watcom).

> The *consequences* of using code whose behavior is undefined depend on
> the compiler and the platform.

Some code behavior is even undefined if you exactly know the platform 
and the compiler. E.g. accessing objects after they have been deleted.


Marcel

[toc] | [prev] | [next] | [standalone]


#6099

FromPete Becker <pete@versatilecoding.com>
Date2011-06-02 10:40 -1000
Message-ID<2011060210404116688-pete@versatilecodingcom>
In reply to#6097
On 2011-06-02 10:34:55 -1000, Marcel Müller said:

> On 02.06.11 22.21, Pete Becker wrote:
>>> So what is
>>> UB in general is not necessarily UB on this specific platform.
>> 
>> Undefined behavior arises from valid code constructs for which the C++
>> language definition does not impose any requirements. That doesn't
>> depend in any way on the compiler that you're using or on the target
>> platform.
> 
> This is UB with respect to the C++ language. The documentation of a 
> specific compiler may define the behavior in cases where the C++ 
> language does not.
> Passing pointer arguments is exactly defined on my platform. So the 
> result of my cast is defined behavior, by all means with the three 
> compilers that are supported (IBM, gnu, Watcom).

Thus utterly missing the point. Again: the C++ langauge definition does 
not tell you what happens when you do this, so the behavior is 
undefined.

> 
>> The *consequences* of using code whose behavior is undefined depend on
>> the compiler and the platform.
> 
> Some code behavior is even undefined if you exactly know the platform 
> and the compiler. E.g. accessing objects after they have been deleted.
> 

Code whose behavior is undefined has undefined behavior *even if* you 
exactly know the platform and the compiler, and *even if* the compiler 
defines what happens. Undefined behavior is about what the C++ standard 
requires, not about what a particular compiler does or doesn't do. In 
particular, undefined behavior is not a synonym for "something bad will 
probably happen".

-- 
  Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The 
Standard C++ Library Extensions: a Tutorial and Reference 
(www.petebecker.com/tr1book)

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web