Though implementation problem

This is a discussion on Though implementation problem within the C++ Programming forums, part of the General Programming Boards category; What's the relationship between CStringEx, CTmplStringBase and CTmplStringBaseTmpl again?...

  1. #256
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    What's the relationship between CStringEx, CTmplStringBase and CTmplStringBaseTmpl again?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  2. #257
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Oh yeah, sorry about that >_<
    CStringEx is the typedef name for the actual class named CTmplStringBase:
    Code:
     typedef CTmplStringBase< char, StrTraits<char> > CStringExA;
    	typedef CTmplStringBase< wchar_t, StrTraits<wchar_t> > CStringExW;
    	typedef CTmplStringBase< TCHAR, StrTraits<TCHAR> > CStringEx;
    And the rest are macros:
    Code:
    #	define CTmplStringBaseTmpl Strings::CTmplStringBase<T, Traits>
    #	define CTmplStringBaseTemplate template<typename T, typename Traits>
    To get rid of the long nasty template lists.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #258
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    You do realize that CTmplStringBaseTemplate is not significantly shorter than template<typename T, typename Traits>, but several magnitudes less readable, right?

    This is indeed weird.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #259
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Quote Originally Posted by CornedBee View Post
    You do realize that CTmplStringBaseTemplate is not significantly shorter than template<typename T, typename Traits>, but several magnitudes less readable, right?
    Perhaps, but it helps me a lot since I don't have to go to 100 places and change the code whenever it changes. Plus it's just one of many, sometimes very long.

    But I also tried this. "The simplest possible snippet of code that compiles".

    Code:
    template<typename T> class CTest { public: CTest() {} CTest(const wchar_t*) {} };
    template<typename T> void operator += (CTest<T>&, const CTest<T>&) {}
    
    void Help()
    {
    	CTest<wchar_t> a;
    	a += L"test";
    }
    Will also give error about unable to deduce the template T.
    If I remove the template and explicitly specify wchar_t, it compiles.
    If I explicitly specify the constructor, such as a += CTest<wchar_t>(L"test"), it works too, but not with the templates.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #260
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Well, GCC agrees with VC++, even though it's even less informative. Simplest solution is apparently to just make += a member function. Or overload the free operator += for const wchar_t*.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #261
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Hm. Weird.
    Either a member or an overload for each of the types it can accept...
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #262
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,359
    I must be doing something wrong here...
    Yep, you are trying to make the compiler guess when it can't.

    To get rid of the long nasty template lists.
    You'd better hope that you don't need to extend a series.

    This is indeed weird.
    It isn't weird, but it is annoying.

    Also, 'operator +=' should really be a method.

    Anyway, the reason this can't work: any specialization or even instantiation of 'CTmplStringBase' may have a constructor that takes a 'const wchar_t *' as an argument. How would the constructor choose between these instantiations?

    You have a few options (for this and the other operators):

    1): Create a less ambiguous interface that forwards to the correct implementation--by explicitly instantiating the template function.

    2): Create the function as a method, losing "left hand conversion".

    3): Get partial "left hand" conversion by declaring the function as a friend and implementing it "inline"--not 'inline'.

    4): My way, or less arrogantly: the way of the inspiring Andrei Alexandrescu. (Note: You lose implicit 'CTmplStringBase<X>' to 'CTmplStringBase<Y>' conversion, but this too can be overcome by adding in a little '1'.)

    Soma

    Code:
    template<typename T> class CTest { public: CTest() {} CTest(const wchar_t*) {} typedef CTest<T> this_type;};
    template<typename T> void operator += (CTest<T>&, const typename CTest<T>::this_type &){}
    
    void Help()
    {
    	CTest<wchar_t> a;
    	a += L"test";
    }

  8. #263
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    the way of the inspiring Andrei Alexandrescu.
    I knew there was such a thing as suppressing argument detection from the second parameter, but I couldn't remember how. My attempt 'const CTest<typename identity<T>::type>&' failed.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #264
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,359
    (Note: You lose implicit 'CTmplStringBase<X>' to 'CTmplStringBase<Y>' conversion, but this too can be overcome by adding in a little '1'.)
    Sorry. That was just poor wording. You obviously keep conversion; what you don't keep is type information.

    *shrug*

    Soma

  10. #265
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Quote Originally Posted by phantomotap View Post
    Yep, you are trying to make the compiler guess when it can't.
    Apparently, that's the only I can figure.
    But I would assume the compiler should be able to guess, even in this instance.

    Also, 'operator +=' should really be a method.
    Maybe you're right about that. I'll move it inside the class, but there are more free operators that would break, and besides, it's just a test. Yesssss...

    Anyway, the reason this can't work: any specialization or even instantiation of 'CTmplStringBase' may have a constructor that takes a 'const wchar_t *' as an argument. How would the constructor choose between these instantiations?
    It would seem to me that if that is the case, then it would be ambiguous, but not when there is only one instance and no specializations.

    The way I see it:
    The compiler sees that it wants a class CTest of type X.
    Therefore the compiler looks at the class for CTest.
    It looks for a constructor to take one argument.
    It finds a constructor that takes const T*. Seeing as I add a wchar_t*, the compiler can easily deduce the type of T to wchar_t and call the constructor.
    The temporary object will then be of type CTest<wchar_t> and the compiler can deduce the type of T for the operator.

    But apparently my analogy is incorrect.

    4): My way, or less arrogantly: the way of the inspiring Andrei Alexandrescu. (Note: You lose implicit 'CTmplStringBase<X>' to 'CTmplStringBase<Y>' conversion, but this too can be overcome by adding in a little '1'.)
    More complexity and tricks to put in the book
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #266
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,359
    O_o

    It would seem to me that if that is the case, then it would be ambiguous, but not when there is only one instance and no specializations.
    A particular template is "compiled" when the definition is encountered and only "instantiated" later. A specialization can be crafted after the specialization is used. The source isn't ambiguous because the compiler can't even determine where to start looking for ambiguities; the compiler knows that an unlimited number of cases may yet exist that would yield a match. How would the compiler determine what types a particular template class should be instantiated with in order to examine the interface for a particular constructor?

    The compiler sees that it wants a class CTest of type X.
    No, the compiler only sees that the "left hand" argument is a 'CTest<X>'.

    Therefore the compiler looks at the class for CTest.
    I think this is your biggest problem. There is no class 'CTest'. There is a class named 'CTest<X>', a class named 'CTest<Y>', and an infinite number of other classes named 'CTest<?>'.

    It looks for a constructor to take one argument.
    Where? The compiler can't look at 'CTest'. It can only look at 'CTest<?>' for any given '?'. How would the compiler know which 'CTest<?>' to examine?

    It finds a constructor that takes const T*. Seeing as I add a wchar_t*, the compiler can easily deduce the type of T to wchar_t and call the constructor.
    A human can rationalize circular logic, but a compiler rather likes things to be composed linearly. The compiler can't find a constructor that takes a 'const T*' because it doesn't know where to look. The compiler can't deduce the type of the template parameter because it doesn't know if any instantiation supports such a constructor. (The compiler literally doesn't know the layout of a template class until it is instantiated.)

    The temporary object will then be of type CTest<wchar_t> and the compiler can deduce the type of T for the operator.
    This, again, is composed of circular logic. If the compiler can't determine the type of 'T' it can't create a temporary of type 'CTest<wchar_t>' in the first place.

    For the foreseeable future, keep this thought in your head when programming with templates: only the instantiations of templates actually exist.

    Soma
    Last edited by phantomotap; 05-26-2008 at 05:02 AM. Reason: Elysia

  12. #267
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Quote Originally Posted by phantomotap View Post
    I think this is your biggest problem. There is no class 'CTest'. There is a class named 'CTest<X>', a class named 'CTest<Y>', and an infinite number of other classes named 'CTest<?>'.
    I think that is the problem right there. The compiler is too inflexible. The class should not be named CTest<?>, but CTest, because however you look at it, there will only be one CTest and its specializations. Therefore, the compiler can scan for any template classes whose class name is CTest, even though it may not exist.
    But I guess we don't have that luxury.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #268
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,359
    I think that is the problem right there. The compiler is too inflexible. The class should not be named CTest<?>, but CTest, because however you look at it, there will only be one CTest and its specializations. Therefore, the compiler can scan for any template classes whose class name is CTest, even though it may not exist.
    But I guess we don't have that luxury.
    O_O

    You expect a compiler to examine a class that does not yet even potentially exist? (Edit: Yes CornedBee, that was derisive.)

    And anyway, that still doesn't answer the crucial question: where does the compiler start its examination? If you were to call every 'CTest<?>' by the name 'CTest', how would the compiler determine if any 'CTest' had a constructor taking a 'const wchar_t *'? (Remember, you just named the template argument relevant 'CTest<wchar_t>' constructor as taking a 'const self::arguments::_1 *' which may or may not be the same as 'const wchar_t *'.) If you look at every 'CTest<?>' by the name 'CTest', and a construct taking a 'const wchar_t *' is found, how do you determine the template arguments over any other valid arguments? (That is, how would you determine the types to pass to the parameters of the template if a partial specialization exists that has a constructor taking a 'const wchar_t *' regardless of the types the template is instantiated with?)

    By the by, this is also why you need 'template<typename T> void operator += (const typename CTest<T>::this_type &, CTest<T>&);'. Even with the existence of the companion operator the compiler can't know where to being, so it doesn't try.

    Soma
    Last edited by phantomotap; 05-26-2008 at 06:28 AM.

  14. #269
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Quote Originally Posted by phantomotap View Post
    You expect a compiler to examine a class that does not yet even potentially exist? (Edit: Yes CornedBee, that was derisive.)
    But there is the thing - why should templates by special? It you provide a class definition, then the class would indeed exist.
    So even if we append template before the class, it should be considered as a valid definition. The class CTest would exist, but not CTest<?> (no CTest<int>, no CTest<float>). Basically, no fully qualified class (full class type) would exist, but the general declaration of the template class, named CTest does exist.

    And anyway, that still doesn't answer the crucial question: where does the compiler start its examination? If you were to call every 'CTest<?>' by the name 'CTest', how would the compiler determine if any 'CTest' had a constructor taking a 'const wchar_t *'?
    Again, why should templates be special? It can search from the line upwards to find a definition of CTest. If it does not exist in the current file, then a header with the definition could be included.
    If one or more specializations of the class does not exist in that header, then it's a programmer error, because this is just how normal classes works, as well. A full definition, including potential specializations. Then the compiler would be able to find the definition it seeks.

    (Remember, you just named the template argument relevant 'CTest<wchar_t>' constructor as taking a 'const self::arguments::_1 *' which may or may not be the same as 'const wchar_t *'.)
    I'm not sure I understand this one.
    Let's look at a source:
    Code:
    template<typename T> class CTest { CTest(const T*) {} };
    As from what I understand, the compiler cannot create a CTest without explicit type (eg CTest<char>) because it doesn't know beforehand what function you are going to call. But if it could search inside the class for a function that takes one argument...
    If it finds one, then it could try to match the argument I pass against T and see if it can generate an implicit conversion to const T*:
    Code:
    template<typename T> class CTest { CTest(const T*) {} };
    int main()
    {
    CTest test("my test");
    }
    In this code, the compiler should search for a constructor that takes one argument.
    Then it can translate T = char* (because it's the argument we pass), so the constructor becomes:
    CTest(const char*)
    Then it will see if it can implicitly convert the argument we pass to that type. If it is successful, it calls the constructor and everyone is happy.

    But if it cannot match the type, then it must complain it cannot deduce T.
    If it finds several functions that may match, then it must complain ambiguous.

    If you look at every 'CTest<?>' by the name 'CTest', and a construct taking a 'const wchar_t *' is found, how do you determine the template arguments over any other valid arguments? (That is, how would you determine the types to pass to the parameters of the template if a partial specialization exists that has a constructor taking a 'const wchar_t *' regardless of the types the template is instantiated with?)
    If that's the case (if the compiler sees those specializations, that is), then the compiler would obviously be confused and return an ambiguous error.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #270
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,359
    But there is the thing - why should templates by special? It you provide a class definition, then the class would indeed exist.
    Templates are templates. They aren't special.

    So even if we append template before the class, it should be considered as a valid definition. The class CTest would exist, but not CTest<?> (no CTest<int>, no CTest<float>). Basically, no fully qualified class (full class type) would exist, but the general declaration of the template class, named CTest does exist.
    And thus, the other problem: you can't say that the constructor taking a 'const the_same_type_as_the_first_template_argument *' of 'CTest' actually takes a 'const wchar_t *' because you've not yet assigned those arguments values/types. Without a fully instantiated template you can't determine if the referenced types of the template arguments support any construct.

    Again, why should templates be special? It can search from the line upwards to find a definition of CTest. If it does not exist in the current file, then a header with the definition could be included.
    I have no idea what you mean to be saying. C++ doesn't parse anything upwards.

    If one or more specializations of the class does not exist in that header, then it's a programmer error, because this is just how normal classes works, as well. A full definition, including potential specializations. Then the compiler would be able to find the definition it seeks.
    Requiring that a specialization appears before its first use changes nothing about how a compiler might determine which specialization to instantiate.

    I'm not sure I understand this one.
    It's simple. You can't examine the values associated with the parameters of a function before it is called; by the same logic, you can't determine the types associated with template parameters until it is instantiated.

    As from what I understand, the compiler cannot create a CTest without explicit type (eg CTest<char>) because it doesn't know beforehand what function you are going to call.
    Nope. It's much simpler than that. The compiler can't create a 'CTest' without an explicit type period. It can't because it is a template by which other types are crafted.

    But if it could search inside the class for a function that takes one argument...
    The compiler can and does search inside a class for conversion functions. That isn't the problem. The compiler doesn't know which class to search.

    If it finds one, then it could try to match the argument I pass against T and see if it can generate an implicit conversion to const T*:
    A generic 'T' matches everything. If I have a partial specialization of 'CTest', before the point of invocation or later in the source, named 'template <typename T> CTest<type_vector<T, T>>' that "always" has a constructor taking 'const wchar_t *' as a parameter how do you determine what value 'T' should have? You can't. The compiler can't, and the compiler doesn't try.

    Then it will see if it can implicitly convert the argument we pass to that type. If it is successful, it calls the constructor and everyone is happy.
    Your example is logically wrong, but I'll give you the befit of doubt. (That is, I'll assume your going to correct the inconsistencies.) I'll even give you some "source"--like yours--of my own for you to chew over. Actually, I'll give you two different chunks.

    Code:
    template <typename T> class CTest {CTest(const T*){} typedef T type;};
    template <typename T> class CTest<type_vector<T, T>> {CTest(const char *){} typedef type_vector<T, T> type;};
    int main()
    {
    CTest test("my test");
    }
    Which template class did I intend to instantiate?
    Which instantiation is more appropriate? Why?
    What would 'type' be a typedef for? And in the other case?

    Code:
    template <typename T> class CTest {CTest(){} typedef T type;}; // Note: Constructor
    template <typename T> class CTest<type_vector<T, T>> {CTest(const char *){} typedef type_vector<T, T> type;};
    int main()
    {
    CTest test("my test");
    }
    There is no ambiguity here; what would 'type' be a typedef for?

    But if it cannot match the type, then it must complain it cannot deduce T.
    That is what the compiler said. The only difference is that the compiler never made it to the constructor call because it could not deduce the type of the class to search for a specific constructor.

    If it finds several functions that may match, then it must complain ambiguous.
    It couldn't determine the type to instantiate the template class with so it couldn't begin looking for ambiguities; the compiler couldn't complain of ambiguities because it couldn't be sure of any.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. WS_POPUP, continuation of old problem
    By blurrymadness in forum Windows Programming
    Replies: 1
    Last Post: 04-20-2007, 07:54 PM
  2. Laptop Problem
    By Boomba in forum Tech Board
    Replies: 1
    Last Post: 03-07-2006, 06:24 PM
  3. implementation file
    By bejiz in forum C++ Programming
    Replies: 5
    Last Post: 11-28-2005, 01:59 AM
  4. Replies: 5
    Last Post: 11-07-2005, 11:34 PM
  5. Memory Problem - I think...
    By Unregistered in forum C Programming
    Replies: 4
    Last Post: 10-24-2001, 01:14 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21