Thread: templates and nameless namespaces

  1. #1
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149

    templates and nameless namespaces

    Given this kind of code structure:
    Code:
    template <T> class Base{}
    namespace {
    template <T> class Derived: public Base<T>{}
    }
    
    template <T>
    class Holder{
         Base<T> *held;
         Holder(){
              held = ???
         }
    }
    How do I elegantly make Holder hold a different version of Derived, depending on which compilation unit the constructor was called in?
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    depending on which compilation unit the constructor was called in?
    Any code that did this would violate the ODR and be undefined.
    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

  3. #3
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    I realize that I can't just put something in place of the ??? to make it work. At least not something simple.

    A solution seems to be to force the user to pass the Derived object explicitly as a parameter. That shouldn't violate ODR. But it's not very elegant.
    Last edited by King Mir; 05-31-2008 at 11:33 AM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> ...make Holder hold a different version of Derived, depending on which compilation unit the constructor was called in?
    Why? Perhaps other options can be explored.

    In terms of doing something *automatically* on a per-compilation unit basis, the first thing that comes to mind is something like the __FILE__ macro in MSVC.

    If it's not auto-magic, then yeah, the user could pass in the instance to be held - or they could provide the *type* to be held as another template parameter.

    gg

  5. #5
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by Codeplug View Post
    >> ...make Holder hold a different version of Derived, depending on which compilation unit the constructor was called in?
    Why? Perhaps other options can be explored.
    It has to do with this thread:
    http://cboard.cprogramming.com/showthread.php?t=103065

    This is the best solution to that problem that I have come up with so far. Or it will be if I can get it to work. This is part of a development on the last solution I posted there.

    In terms of doing something *automatically* on a per-compilation unit basis, the first thing that comes to mind is something like the __FILE__ macro in MSVC.
    I'm aware of the __FILE__ macro, but I can't think of a way to use it here.

    If it's not auto-magic, then yeah, the user could pass in the instance to be held - or they could provide the *type* to be held as another template parameter.
    I'm not sure what you mean by auto-magic.

    I can't have Holder have another template parameter because that would make different holder objects incompatible. Unless . . . I'll have to think about that.

    I can make a template parameter for the constructor, but that would have the same problems as a regular argument: why should the user have to pass an argument if the argument is always the same.
    Last edited by King Mir; 05-31-2008 at 01:26 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  6. #6
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I didn't read the relevant thread, but I'm fairly certain this will not work to do what you want. (I don't even really care what you want to do.) It has to do with the way compilers and linkers handle templates and the mysterious "anonymous" 'namespace'. Unfortunately, it is neither undefined nor dependable. It just happens that compilers and linkers don't manage everything correctly. Worse, what you get depends on the order various files are compiled/linked.

    Trust me on this: you want to compile the following code chunks as separate files, link them in various orders, and then execute them. The results aren't correct and consistent on any compiler suite I've used--and I've used a lot. Yes, "test_a.cxx" and "test_b.cxx" are nearly identical, but that's the point.

    Soma

    (tester.cxx):
    Code:
    void test_a();
    void test_b();
    
    int main()
    {
      
      test_a();
      test_b();
      return(0);
    }
    (test_a.cxx):
    Code:
    #include <iostream>
    #if defined(__GNUC__)
      #include <cxxabi.h>
    #endif
    
    template
    <
      typename type_F
    >
    struct base
    {
      virtual void go() = 0;
    };
    
    namespace
    {
      template
      <
        typename type_F
      >
      struct derived: public base<type_F>
      {
        virtual void go()
        {
          std::cout << __FILE__ << '\n';
        }
      };
    }
    
    template
    <
      typename type_F
    >
    struct holder
    {
      holder()
      {
        p = new derived<type_F>;
      }
      ~holder()
      {
        delete p;
      }
      void go()
      {
        #if defined(__GNUC__)
          int status(0);
          std::cout << abi::__cxa_demangle(typeid(*p).name(), 0, 0, &status) << ':';
        #endif
        #if !defined(__GNUC__)
          std::cout << typeid(*p).name() << ':';
        #endif
        p->go();
      }
      base<type_F> * p;
    };
    
    void test_a()
    {
      std::cout << "Test A:";
      holder<int> h;
      h.go();
    }
    (test_b.cxx):
    Code:
    #include <iostream>
    #if defined(__GNUC__)
      #include <cxxabi.h>
    #endif
    
    template
    <
      typename type_F
    >
    struct base
    {
      virtual void go() = 0;
    };
    
    namespace
    {
      template
      <
        typename type_F
      >
      struct derived: public base<type_F>
      {
        virtual void go()
        {
          std::cout << __FILE__ << '\n';
        }
      };
    }
    
    template
    <
      typename type_F
    >
    struct holder
    {
      holder()
      {
        p = new derived<type_F>;
      }
      ~holder()
      {
        delete p;
      }
      void go()
      {
        #if defined(__GNUC__)
          int status(0);
          std::cout << abi::__cxa_demangle(typeid(*p).name(), 0, 0, &status) << ':';
        #endif
        #if !defined(__GNUC__)
          std::cout << typeid(*p).name() << ':';
        #endif
        p->go();
      }
      base<type_F> * p;
    };
    
    void test_b()
    {
      std::cout << "Test B:";
      holder<int> h;
      h.go();
    }

  7. #7
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    That violates ODR (One Definition Rule) when you do this:
    Code:
    holder()
    {
        p = new derived<type_F>;
    }
    new derived<type_F> means a different thing for each compilation unit, even though it looks the same. So the definition of holder varies in different compilation units, which is not allowed.

    Actually I'm not sure that this is technically ODR. But the name doesn't matter. Point is, that is undefined, and not a problem with compilers.

    Doing it that way won't work. I'm trying to find a way that does.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    It doesn't violate the ODR specification in any way, form, or fashion. It isn't undefined in any way, form, or fashion. I could have put the relevant bits in different headers, imposed a bit of indirection by using traits, and derived a specialization of a trait inside the "anonymous" 'namespace' private to each translation unit. It doesn't matter; this was just an example; the results would be the same. The ODR says, among other things, that a given template specialization is a distinct entity from any other specialization, a given template instantiation is a distinct entity from any other instantiation, and an entity declared/defined in the "anonymous" 'namespace' is separate from any other similar entity in any other translation unit. Every step of the instantiation is well defined by the standard. The compiler/linker should and usually does generate a unique entity in instantiating a template modeled upon "private" types. Unfortunately, because of the way compilers and linkers work, the entity generated from such a construct as above, unless directly instantiated with a "private" type, will be externally visible. Worse, and for the same reasons, the resulting entities generated from distinct translation units will appear the same. Even worse, and again for the same reasons, this sameness will not generate an ODR warning/error even though it should in such a case.

    If you actually instantiate a given template with a "private" type, manually telling the compiler that the instantiation will be modeled on a private type, then everything is fine.

    Soma
    Last edited by phantomotap; 05-31-2008 at 03:28 PM.

  9. #9
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Yeah, looking on wiki, this is not ODR. ODR refers to redefining the same functiom, class or whatever in the same transaction unit, not having the same template have differing definition based on compilation units. Unlike ODR, the compiler is not required to give an error here.

    But still, in your example the holder object has different constructors for test_a.cxx and test_b.cxx. That's not allowed. The compiler is only instantiating public base<type_F> once, because it is only use once: inside the one instantiation of holder. You can add following line into each of the test cases:
    Code:
    derived<int> derivedEg;
    The then derived<int> would be instantiated twice. Similarly you can explicitly instantiate it in each of the two files. But all copies of holder would hold only one of the two derived instantiations. (or the compiler can do something else entirely, like start a game of Rogue)

    It may be true that Compliers and Linkers do not deal with templates in nameless namespaces correctly, but this example does not show it. Can you show me an example that does?
    Last edited by King Mir; 05-31-2008 at 04:54 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    This is totally a ODR violation.

    First, let's review the ODR. Section 3.2 of the standard. Paragraph 1 is about a definition not appearing more than once in a translation unit. Paragraph 2 defines the terms "potentially evaluated" and "used". Paragraph 3 is about normal functions appearing only once in a program. Paragraph 4 says that a class definition must appear in a translation unit if the code requires the class to be complete; it also defines when code needs the complete definition.

    Paragraph 5 is interesting. It's too long to quote, but it says basically that definitions of classes, enums, inline functions, templates and partial specializations may appear more than once in the same program (but not more than once per compilation unit), provided that
    1) the definitions are all identical on the parser level ("same sequence of tokens") and
    2) every token has the exact same meaning: bind to the same function or type, etc.
    It also says that no diagnostic is required from the compiler.

    Let's see, then. test_a.cxx and test_b.cxx both contain three template definitions. Two have the same name: ::base and ::holder. unnamed_of_test_a_cxx::derived and unnamed_of_test_b_cxx::derived are distinct types.

    The ::base definitions have the same token sequence in both files. No tokens bind to any name. This one is clean.

    The ::derived definitions have the same token sequence in both files. But the first name that binds is derived in "p = new derived<type_F>;". This name binds to unnamed_of_test_a_cxx::derived in test_a.cxx and to unnamed_of_test_b_cxx::derived in test_b.cxx. Different meaning in the two definitions. It's an ODR violation, undefined behaviour, no diagnostic required.
    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

  11. #11
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    As I've said... nevermind; just read the post again. Specifically this isn't an ODR violation because the relevant type is not in the relevant scope of the relevant template class and the relevant reference is not dependent on any name of the relevant template class. If compilers referenced the "anonymous" 'namespace' directly this would not compile. If linkers understood templates better this would not link.

    I'll just take further response simply with: "Paragraph 5 is interesting."

    Paragraph 3.2.5 of the standard doesn't apply to "private" definitions, fully specialized templates, or non-dependent template names that aren't in the enclosing scope.

    Also, I'm about to start playing "Oblivion". Now, I'll be checking back in a bit, but if the only thing offered is "OMG. YOUR THE WRONG!" don't expect me to take time to respond. I will not take time to respond to repeats either. You're obviously free to disagree again; I wouldn't hold that against you. I just will not take time to respond with repeats of my own. I'm willing to be proven wrong, but it would have to be a different and unfamiliar part of the standard. Basically unless you can show that differing fully specialized templates and "private" definitions are both in violation of the ODR and/or undefined behavior then you shouldn't bother.

    If you actually instantiate a given template with a "private" type, manually telling the compiler that the instantiation will be modeled on a private type, then everything is fine.
    I had just turned on my XBox 360 when I thought of a problem. That seems as if I'm saying the relevant type must be a "private" type, but that isn't the case. I thought I would clear it up. You don't have to change anything relevant to allow the source to run correctly; you only need to make sure the template is mangled as "private". The code provided should be a sufficient demonstration; you only need to change which line is commented out.

    Soma

    Code:
    #include <iostream>
    #if defined(__GNUC__)
      #include <cxxabi.h>
    #endif
    
    template
    <
      typename type_F
    >
    struct base
    {
      virtual void go() = 0;
    };
    
    namespace
    {
      template
      <
        typename type_F
      >
      struct derived: public base<type_F>
      {
        virtual void go()
        {
          std::cout << __FILE__ << '\n';
        }
      };
      struct i_am_private{};
    }
    
    template
    <
      typename type_F,
      typename type2
    >
    struct holder
    {
      holder()
      {
        p = new derived<type_F>;
      }
      ~holder()
      {
        delete p;
      }
      void go()
      {
        #if defined(__GNUC__)
          int status(0);
          std::cout << abi::__cxa_demangle(typeid(*p).name(), 0, 0, &status) << ':';
        #endif
        #if !defined(__GNUC__)
          std::cout << typeid(*p).name() << ':';
        #endif
        p->go();
      }
      base<type_F> * p;
    };
    
    void test_a()
    {
      std::cout << "Test A:";
      holder<int, float> h;
      //holder<int, i_am_private> h;
      h.go();
    }
    Code:
    #include <iostream>
    #if defined(__GNUC__)
      #include <cxxabi.h>
    #endif
    
    template
    <
      typename type_F
    >
    struct base
    {
      virtual void go() = 0;
    };
    
    namespace
    {
      template
      <
        typename type_F
      >
      struct derived: public base<type_F>
      {
        virtual void go()
        {
          std::cout << __FILE__ << '\n';
        }
      };
      struct i_am_private{};
    }
    
    template
    <
      typename type_F,
      typename type2
    >
    struct holder
    {
      holder()
      {
        p = new derived<type_F>;
      }
      ~holder()
      {
        delete p;
      }
      void go()
      {
        #if defined(__GNUC__)
          int status(0);
          std::cout << abi::__cxa_demangle(typeid(*p).name(), 0, 0, &status) << ':';
        #endif
        #if !defined(__GNUC__)
          std::cout << typeid(*p).name() << ':';
        #endif
        p->go();
      }
      base<type_F> * p;
    };
    
    void test_b()
    {
      std::cout << "Test B:";
      holder<int, float> h;
      //holder<int, i_am_private> h;
      h.go();
    }
    Last edited by phantomotap; 05-31-2008 at 06:55 PM. Reason: Code

  12. #12
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    phantomotap, frankly I did not understand what most of your post said. I just read your code.

    Even now I don't understand what you mean by "private" definitions. This is clearly a not a fully specialized template, and the operation is defendant on the template argument -- defendant lookup is used.
    Last edited by King Mir; 05-31-2008 at 07:53 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  13. #13
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> I just will not take time to respond with repeats of my own.
    CornedBee took the time to lay out what he thinks the standard says and how he thinks your code violates it. You however, basically repeated yourself.

    >> Basically unless you can show that differing fully specialized templates and "private" definitions are both in violation of the ODR
    CornedBee already showed how your duplicate templates violate ODR. "Private definitions" have nothing to do with the rule.

    >> Paragraph 3.2.5 of the standard doesn't apply to "private" definitions, fully specialized templates, or non-dependent template names that aren't in the enclosing scope.
    I don't see those exceptions listed. You'll need to quote some relevant text in the standard to back that up.
    As far as I see it, 3.2.5 applies exactly to what it says it applies to. Since you don't like repeating (and neither do I), I'll quote the standard directly and make my own assertions.
    Quote Originally Posted by ISO/IEC 14882:2003(E), 3.2.5
    There can be more than one definition of a class type (clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (clause 14), non-static function template (14.5.5), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.4) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
    ...
    -- in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after matching of partial template specialization (14.8.3), except that a name can refer to a const object with internal or no linkage if the object has the same integral or enumeration type in all definitions of D, and the object is initialized with a constant expression (5.19), and the value (but not the address) of the object is used, and the object has the same value in all definitions of D;
    ...
    If D is a template, and is defined in more than one translation unit, then the last four requirements from the list above shall apply to names from the template’s enclosing scope used in the template definition (14.6.3), and also to dependent names at the point of instantiation (14.6.2). If the definitions of D satisfy all these requirements, then the program shall behave as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.
    My assertions are:
    1) Both holder's qualify as "an entity named D" in 3.2.5.
    2) And they violate the rule via the text in red. Each is bound to unnamed_of_test_a_cxx::derived and to unnamed_of_test_b_cxx::derived respectively - two serparate entities. There are no exceptions listed for objects of a particular scope or linkage (which I can only assume you mean by "private definition").
    3) Text in blue is in direct contradiction to your "private definitions are an exception" claim.

    If one believes any of my assertions are incorrect, please explain how/why. Misinterpretations of the standard? Or do other parts of the standard grant exceptions to the rule?

    gg
    Last edited by Codeplug; 05-31-2008 at 09:06 PM.

  14. #14
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    This is clearly a not a fully specialized template, and the operation is defendant on the template argument -- defendant lookup is used.
    Well spotted, and did I say that as yet it was a specialized template?

    You however, basically repeated yourself.
    Indeed? O_o

    You'll need to quote some relevant text in the standard to back that up.
    CornedBee doesn't have to quote the standard but I do? O_o

    I would, but I don't really care. I used to care, but such an experiment is valuable only for a while.

    Both unnamed_of_test_a_cxx::derived and unnamed_of_test_b_cxx::derived qualify as "an entity named D" in 3.2.5.
    You don't even understand the rules involved regarding the "anonymous" 'namespace' so aren't really knowledgeable enough to make any assertion.

    And they violate the rule via the text in red.
    Indeed? That is a marvel. I'm grateful for the education. I'm always willing to learn.

    Text in blue is in direct contradiction to your "private definitions are an exception" claim.
    Well... I would explain... but I would be repeating myself.

    If one believes any of my assertions are incorrect, please explain how/why. Misinterpretations of the standard? Or do other parts of the standard grant exceptions to the rule?
    Unfortunately your assertions aren't even interesting in this context as you've misunderstood the role CornedBee has taken. The argument is that it is a violation only because the definition of the 'holder' constructor instantiates in such a way that the definition is different for different translations units because the type instantiated inside the constructor is actually different "private" types from the different translation unit; it isn't even because of the unique entity rule that he has taken this position but because of the rule that that rule applies to the component types used in defining the template recursively as it were.

    Soma
    Last edited by phantomotap; 05-31-2008 at 10:01 PM.

  15. #15
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by phantomotap View Post
    Well spotted, and did I say that as yet it was a specialized template?
    Just covering my bases. Do you mind defining "private" definitions?

    You don't even understand the rules involved regarding the "anonymous" 'namespace' so aren't really knowledgeable enough to make any assertion.
    I admit I never read a book about anonymous namespaces, but I have read a book on templates: C++ Templates: The Complete Guide by David Vandevoorde and Nicolai M. Josuttis. The book provides a remarkably similar example of how unnamed namespace objects can result in violations of the ODR, when argument defendant-lookup is involved. "new derived<type_F>" is clearly argument dependent lookup. But the call refers to two different entities, for each translation unit.

    But I'm repeating myself.

    Well... I would explain... but I would be repeating myself.
    Repetition may be called for. Nobody understood the first time.
    because of the rule that that rule applies to the component types used in defining the template recursively as it were.
    "Defining the template recursively", where is that happening? There is no recursion here.

    From your original explanation:
    Quote Originally Posted by phantomotap View Post
    Worse, and for the same reasons, the resulting entities generated from distinct translation units will appear the same.
    There is only one instance of holder. So, Of course all uses of that instance are the same.



    @Codeplug
    I must disagree with you on the blue text. The highlight should include the sentence immediately following the current highlight. This is dependent lookup. The point of instantiation is where the names must be the same, not at the definition itself.
    Last edited by King Mir; 05-31-2008 at 11:02 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

Popular pages Recent additions subscribe to a feed