CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 30
  1. #1
    John E is offline Elite Member Power Poster
    Join Date
    Apr 2001
    Location
    Manchester, England
    Posts
    4,835

    Where does a template function get instantiated ?

    Consider the following code:

    Code:
    template<class TYPE>
    void MultiplyByTwo(TYPE data)
    {
        cout <<"Double =  " << data * 2 << endl;
    }
    If I declared that code in a header file I'd be able to call it with an int, float, double or whatever. But where does the actual code get instantiated? Is it effectively inlined?

    What I'm wondering is if there'd ever be any scenario for putting such code in a DLL - e.g.

    Code:
    #ifdef BUILDING_MY_DLL
        #define MY_DLL_API __declspec(dllexport)
    #else
        #define MY_DLL_API __declspec(dllimport)
    #endif 
    
    
    template<class TYPE>
    MY_DLL_API void MultiplyByTwo(TYPE data)
    {
        cout <<"Double =  " << data * 2 << endl;
    }
    I just tried it and was slightly surprised to find it wouldn't compile. It compiles fine when actually building the DLL but when I try to build something else which uses that DLL I get compiler error C2491 (definition of dllimport function not allowed).

    I guess that kinda makes sense if template functions are effectively inlined... or is there some other explanation
    "A problem well stated is a problem half solved.” - Charles F. Kettering

  2. #2
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Where does a template function get instantiated ?

    Quote Originally Posted by John E View Post
    I guess that kinda makes sense if template functions are effectively inlined... or is there some other explanation
    Templates are just that -- templates. The template is not an actual function -- all it does is describe a generic boilerplate for a function. So it makes no sense placing it in a DLL, since it isn't a function at all.

    The function only becomes alive when an instantiation of that template is done. Then the compiler does what it does to create a concrete function. Whether it is inline or not depends on the compiler and compiler options.

    Regards,

    Paul McKenzie

  3. #3
    John E is offline Elite Member Power Poster
    Join Date
    Apr 2001
    Location
    Manchester, England
    Posts
    4,835

    Re: Where does a template function get instantiated ?

    Thanks Paul. Here's the scenario I'm trying to get my head around...

    Suppose some template code needed to access a static variable. How would I make sure that there was only one physical copy of the variable? If I put the code into a DLL that usually guarantees that there'll only be one copy - even if the DLL links to several other modules. But that only really works for regular functions. So is it bad practise for either an inline function or a template to access static data?
    "A problem well stated is a problem half solved.” - Charles F. Kettering

  4. #4
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Where does a template function get instantiated ?

    Quote Originally Posted by John E View Post
    What I'm wondering is if there'd ever be any scenario for putting such code in a DLL - e.g.

    Code:
    #ifdef BUILDING_MY_DLL
        #define MY_DLL_API __declspec(dllexport)
    #else
        #define MY_DLL_API __declspec(dllimport)
    #endif 
    
    
    template<class TYPE>
    MY_DLL_API void MultiplyByTwo(TYPE data)
    {
        cout <<"Double =  " << data * 2 << endl;
    }
    I just tried it and was slightly surprised to find it wouldn't compile. It compiles fine when actually building the DLL but when I try to build something else which uses that DLL I get compiler error C2491 (definition of dllimport function not allowed).

    I guess that kinda makes sense if template functions are effectively inlined... or is there some other explanation
    John, this particular compile error has nothing to do neither with templates nor dlls. It is exactly what the error message says: definition of dllimport function not allowed. Which means, dllimport is applicable for declarations only, and never is for definitions.

    Just consider a plain sample with no template, no dll.

    Code:
    // 96.cpp
    #include <iostream>
    
    using namespace std;
    
    
    __declspec(dllimport) void MultiplyByTwo(int data)
    {
        cout <<"Double =  " << data * 2 << endl;
    }
    
    int main()
    {
        MultiplyByTwo(2);
        return 0;
    }
    Code:
    D:\Temp\96>cl 96.cpp /EHsc
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    96.cpp
    96.cpp(7) : error C2491: 'MultiplyByTwo' : definition of dllimport function not allowed
    You see that? The same story, absolutely. So your "demo" proves effectively nothing, and there was nothing to be surprised about.

    And inlining again has nothing to do with templates.
    Code:
    #include <iostream>
    
    using namespace std;
    
    template<class TYPE>
    void MultiplyByTwo(TYPE data)
    {
        cout <<"Double =  " << data * 2 << endl;
    }
    
    template<class TYPE>
    inline void MultiplyByThree(TYPE data)
    {
        cout <<"Triple =  " << data * 3 << endl;
    }
    
    int main()
    {
        int a = 2;
        MultiplyByTwo(a);
        MultiplyByThree(a);
    
        return 0;
    }
    Code:
    D:\Temp\96>cl 96a.cpp /EHsc /link /map:96a.map
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    96a.cpp
    Microsoft (R) Incremental Linker Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:96a.exe
    /map:96a.map
    96a.obj
    
    D:\Temp\96>96a.exe
    
    Double =  4
    Triple =  6
    But anyway compiler decided not to inline.
    Code:
     96a
    
     Timestamp is 52618590 (Fri Oct 18 23:01:36 2013)
    
     Preferred load address is 00400000
    
     Start         Length     Name                   Class
     0001:00000000 00017afaH .text                   CODE
     0001:00017b00 0000056cH .text$x                 CODE
    . . .
     0003:00000000 00001780H .data                   DATA
     0003:00001780 0000210cH .bss                    DATA
    
      Address         Publics by Value              Rva+Base       Lib:Object
    
     0000:00000000       __except_list              00000000     <absolute>
     0000:00000022       ___safe_se_handler_count   00000022     <absolute>
     0000:00000000       ___ImageBase               00400000     <linker-defined>
     0001:00000000       _main                      00401000 f   96a.obj
     0001:00000030       ??$MultiplyByTwo@H@@YAXH@Z 00401030 f i 96a.obj
     0001:00000060       ?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z 00401060 f i 96a.obj
     0001:00000080       ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z 00401080 f i 96a.obj
     0001:000001b0       ?eq_int_type@?$char_traits@D@std@@SA_NABH0@Z 004011b0 f i 96a.obj
     0001:000001d0       ?eof@?$char_traits@D@std@@SAHXZ 004011d0 f i 96a.obj
     0001:000001e0       ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ 004011e0 f i 96a.obj
    . . .
     0001:00000a20       ??0_Sentry_base@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@AAV12@@Z 00401a20 f i 96a.obj
     0001:00000a80       ??1_Sentry_base@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@XZ 00401a80 f i 96a.obj
     0001:00000ad0       ??$MultiplyByThree@H@@YAXH@Z 00401ad0 f i 96a.obj
     0001:00000b10       ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z 00401b10 f i 96a.obj
     0001:00000b30       ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z 00401b30 f i 96a.obj
    . . .
    Precisely like Paul already said.
    Best regards,
    Igor

  5. #5
    Join Date
    Nov 2003
    Posts
    1,902

    Re: Where does a template function get instantiated ?

    >> Suppose some template code needed to access a static variable.
    Could export the variable. Or provide an accessor function to it.

    Instantiated templates can also be exported, but may not be very useful.
    http://msdn.microsoft.com/en-us/library/twa2aw10.aspx

    gg

  6. #6
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Where does a template function get instantiated ?

    Quote Originally Posted by Igor Vartanov View Post
    [...]

    But anyway compiler decided not to inline.
    Code:
     96a
    
     Timestamp is 52618590 (Fri Oct 18 23:01:36 2013)
    
     Preferred load address is 00400000
    
     Start         Length     Name                   Class
     0001:00000000 00017afaH .text                   CODE
     0001:00017b00 0000056cH .text$x                 CODE
    . . .
     0003:00000000 00001780H .data                   DATA
     0003:00001780 0000210cH .bss                    DATA
    
      Address         Publics by Value              Rva+Base       Lib:Object
    
     0000:00000000       __except_list              00000000     <absolute>
     0000:00000022       ___safe_se_handler_count   00000022     <absolute>
     0000:00000000       ___ImageBase               00400000     <linker-defined>
     0001:00000000       _main                      00401000 f   96a.obj
     0001:00000030       ??$MultiplyByTwo@H@@YAXH@Z 00401030 f i 96a.obj
     0001:00000060       ?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z 00401060 f i 96a.obj
     0001:00000080       ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z 00401080 f i 96a.obj
     0001:000001b0       ?eq_int_type@?$char_traits@D@std@@SA_NABH0@Z 004011b0 f i 96a.obj
     0001:000001d0       ?eof@?$char_traits@D@std@@SAHXZ 004011d0 f i 96a.obj
     0001:000001e0       ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ 004011e0 f i 96a.obj
    . . .
     0001:00000a20       ??0_Sentry_base@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@AAV12@@Z 00401a20 f i 96a.obj
     0001:00000a80       ??1_Sentry_base@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@XZ 00401a80 f i 96a.obj
     0001:00000ad0       ??$MultiplyByThree@H@@YAXH@Z 00401ad0 f i 96a.obj
     0001:00000b10       ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z 00401b10 f i 96a.obj
     0001:00000b30       ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z 00401b30 f i 96a.obj
    . . .
    Precisely like Paul already said.
    I don't think the map file really proves anything here in that respect: Your sample functions are non-static free functions, so they're visible to code outside the module defining them. The compiler must provide non-inlined exported versions of the functions, just in case any code in another module decides to call them. (Inlineness can't be exported of course.) The compiler may still inline calls to these functions made from inside the module defining them. What does the assembly listing say?
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  7. #7
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Where does a template function get instantiated ?

    Quote Originally Posted by Eri523 View Post
    I don't think the map file really proves anything here in that respect: Your sample functions are non-static free functions, so they're visible to code outside the module defining them. The compiler must provide non-inlined exported versions of the functions, just in case any code in another module decides to call them. (Inlineness can't be exported of course.) The compiler may still inline calls to these functions made from inside the module defining them. What does the assembly listing say?
    Please be my guest.

    Code:
    #include <iostream>
    
    using namespace std;
    
    template<class TYPE>
    static void MultiplyByTwo(TYPE data)
    {
        cout <<"Double =  " << data * 2 << endl;
    }
    
    template<class TYPE>
    static inline void MultiplyByThree(TYPE data)
    {
        cout <<"Triple =  " << data * 3 << endl;
    }
    
    int main()
    {
    	int a = 2;
    	MultiplyByTwo(a);
    	MultiplyByThree(a);
    
    	return 0;
    }
    Code:
    D:\Temp\96>cl 96b.cpp /EHsc /link /map:96b.map
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    96b.cpp
    Microsoft (R) Incremental Linker Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:96b.exe
    /map:96b.map
    96b.obj
    
    D:\Temp\96>grep Multiply 96b.map
    96b.map: 0001:00000030       ??$MultiplyByTwo@H@@YAXH@Z 00401030 f   96b.obj
    96b.map: 0001:00000060       ??$MultiplyByThree@H@@YAXH@Z 00401060 f   96b.obj
    Best regards,
    Igor

  8. #8
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Where does a template function get instantiated ?

    Hmmm...

    When I compile that (VC++ 2010 Express, default release mode settings except for turning on assembly listing and map file output), both function calls do get inlined:

    Code:
    PUBLIC	_main
    ; Function compile flags: /Ogtp
    ;	COMDAT _main
    _TEXT	SEGMENT
    _main	PROC						; COMDAT
    
    ; 19   : 	int a = 2;
    ; 20   : 	MultiplyByTwo(a);
    
      00000	a1 00 00 00 00	 mov	 eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
      00005	8b 0d 00 00 00
    	00		 mov	 ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
      0000b	50		 push	 eax
      0000c	6a 04		 push	 4
      0000e	68 00 00 00 00	 push	 OFFSET ??_C@_0L@IAJJIPJN@Double?5?$DN?5?5?$AA@
      00013	51		 push	 ecx
      00014	e8 00 00 00 00	 call	 ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
      00019	83 c4 08	 add	 esp, 8
      0001c	8b c8		 mov	 ecx, eax
      0001e	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
      00024	8b c8		 mov	 ecx, eax
      00026	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
    
    ; 21   : 	MultiplyByThree(a);
    
      0002c	8b 15 00 00 00
    	00		 mov	 edx, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
      00032	a1 00 00 00 00	 mov	 eax, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
      00037	52		 push	 edx
      00038	6a 06		 push	 6
      0003a	68 00 00 00 00	 push	 OFFSET ??_C@_0L@IIFFMGDN@Triple?5?$DN?5?5?$AA@
      0003f	50		 push	 eax
      00040	e8 00 00 00 00	 call	 ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
      00045	83 c4 08	 add	 esp, 8
      00048	8b c8		 mov	 ecx, eax
      0004a	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
      00050	8b c8		 mov	 ecx, eax
      00052	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
    
    ; 22   : 
    ; 23   : 	return 0;
    
      00058	33 c0		 xor	 eax, eax
    
    ; 24   : }
    
      0005a	c3		 ret	 0
    _main	ENDP
    _TEXT	ENDS
    END
    For reasons I don't know, despite the static, the compiler output still contains non-inlined versions of the functions that I dodn't see being called anywhere:

    Code:
    ; Function compile flags: /Ogtp
    ; File c:\users\eri\documents\visual studio 2010\projects\test12\test12\test12.cpp
    ;	COMDAT ??$MultiplyByThree@H@@YAXH@Z
    _TEXT	SEGMENT
    ??$MultiplyByThree@H@@YAXH@Z PROC			; MultiplyByThree<int>, COMDAT
    
    ; 14   :     cout <<"Triple =  " << data * 3 << endl;
    
      00000	a1 00 00 00 00	 mov	 eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
      00005	8b 0d 00 00 00
    	00		 mov	 ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
      0000b	50		 push	 eax
      0000c	6a 06		 push	 6
      0000e	68 00 00 00 00	 push	 OFFSET ??_C@_0L@IIFFMGDN@Triple?5?$DN?5?5?$AA@
      00013	51		 push	 ecx
      00014	e8 00 00 00 00	 call	 ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
      00019	83 c4 08	 add	 esp, 8
      0001c	8b c8		 mov	 ecx, eax
      0001e	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
      00024	8b c8		 mov	 ecx, eax
      00026	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
    
    ; 15   : }
    
      0002c	c3		 ret	 0
    ??$MultiplyByThree@H@@YAXH@Z ENDP			; MultiplyByThree<int>
    ; Function compile flags: /Ogtp
    _TEXT	ENDS
    ;	COMDAT ??$MultiplyByTwo@H@@YAXH@Z
    _TEXT	SEGMENT
    ??$MultiplyByTwo@H@@YAXH@Z PROC				; MultiplyByTwo<int>, COMDAT
    
    ; 8    :     cout <<"Double =  " << data * 2 << endl;
    
      00000	a1 00 00 00 00	 mov	 eax, DWORD PTR __imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
      00005	8b 0d 00 00 00
    	00		 mov	 ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
      0000b	50		 push	 eax
      0000c	6a 04		 push	 4
      0000e	68 00 00 00 00	 push	 OFFSET ??_C@_0L@IAJJIPJN@Double?5?$DN?5?5?$AA@
      00013	51		 push	 ecx
      00014	e8 00 00 00 00	 call	 ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
      00019	83 c4 08	 add	 esp, 8
      0001c	8b c8		 mov	 ecx, eax
      0001e	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
      00024	8b c8		 mov	 ecx, eax
      00026	ff 15 00 00 00
    	00		 call	 DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
    
    ; 9    : }
    
      0002c	c3		 ret	 0
    ??$MultiplyByTwo@H@@YAXH@Z ENDP				; MultiplyByTwo<int>
    _TEXT	ENDS
    They don't show up in the map file, though.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  9. #9
    John E is offline Elite Member Power Poster
    Join Date
    Apr 2001
    Location
    Manchester, England
    Posts
    4,835

    Re: Where does a template function get instantiated ?

    Thanks for all the suggestions guys. The problem has morphed somewhat during the past couple of days. My earlier problem (how to make sure the template function only had one instance of a static data member) has gone away now, since I can use an accessor function, like Codeplug suggested. But I still need to figure out where exactly template functions get instantiated.

    Basically I'm trying to convert some code that was originally written for gcc. To boil it down to its simplest level the code builds 2 x DLLs. The source code for one DLL contains all the templates (let's call it DLLA) but the other one (DLLB) seems to use them. Here's what the code originally looked like:-

    Code:
    // This template is in DLL A
    namespace DLLA {
    
    template<typename Time>
    class Event {
    public:
    	Event (EventType type=0, Time time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false);
    
    	Event(const Event& copy, bool alloc);
    
    	~Event();
    
    	const Event& operator=(const Event& copy);
    
    	void set(const uint8_t* buf, uint32_t size, Time t);
    
    	// Blah blah blah. loads of other stuff
    };
    
    }
    The parameter Time can be one of several types (int, double, int64_t etc). Which types are needed isn't known until we build DLL B:-

    Code:
    // These functions are in DLL B
    void func_A (DLLA::Event<double>&   a_event);
    void func_B (DLLA::Event<inr>&      b_event);
    void func_C (DLLA::Event<int64_t>&  c_event);
    If I build the code without any modification, DLL B has no access to DLLA::Event. Nothing got exported from DLL A so they all come up as unresolved externals. If DLL::Event wasn't a template class this would be the quickest way to solve the problem:-

    Code:
    namespace DLLA {
    
    class DLLA_API Event {
    public:
    	Event (EventType type=0, int time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false);
    	Event (EventType type=0, double time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false);
    	Event (EventType type=0, int64_t time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false);
    	Event(const Event& copy, bool alloc);
    
    	~Event();
    
    	const Event& operator=(const Event& copy);
    
    	void set(const uint8_t* buf, uint32_t size, int t);
    	void set(const uint8_t* buf, uint32_t size, double t);
    	void set(const uint8_t* buf, uint32_t size, int64_t t);
    	// Blah blah blah. loads of other stuff
    };
    
    }
    (assume that DLLA_API will import or export the symbols, as required). So you might imagine that this would work for a template class:-

    Code:
    namespace DLLA {
    
    template<typename Time>
    class DLLA_API Event {
    public:
    	Event (EventType type=0, Time time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false);
    
    	Event(const Event& copy, bool alloc);
    
    	~Event();
    
    	const Event& operator=(const Event& copy);
    
    	void set(const uint8_t* buf, uint32_t size, Time t);
    
    	// Blah blah blah. loads of other stuff
    };
    
    }
    but unfortunately, it doesn't...

    This does seem to work....

    Code:
    namespace DLLA {
    
    template<typename Time>
    class Event {
    public:
    	DLLA_API Event (EventType type=0, Time time=0, uint32_t size=0, uint8_t* buf=NULL, bool alloc=false);
    
    	DLLA_API Event(const Event& copy, bool alloc);
    
    	DLLA_API ~Event();
    
    	DLLA_API const Event& operator=(const Event& copy);
    
    	DLLA_API void set(const uint8_t* buf, uint32_t size, Time t);
    
    	// Blah blah blah. loads of other stuff
    };
    
    }
    At least, both DLLs will compile and link. I haven't got as far as testing them yet. So if the worst comes to the worst, it looks like I can make this work by specifically exporting any required symbols from DLL A (BTW, the actual functions are implemented in a cpp source file in DLLA). But when building DLL A, how does it know that sometime subsequently, it's going to need classes which deal with double, int and int64_t? When building DLL A, surely the compiler won't know that as it hasn't encountered DLL B's code yet

    Or am I over-thinking this??
    "A problem well stated is a problem half solved.” - Charles F. Kettering

  10. #10
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Where does a template function get instantiated ?

    The reality behind it.
    Templates get instantiated as part of the code module currently being compiled. A linker would then remove duplicates from the objects being linked together. modern compilers however tend to postpone the actual instantiation to a pre-linking stage when they know what template instances are needed. THis assumes the compiler and linker "work together" on generating code.

    If you define a template as part of the set of headers and classes that make up a DLL project. Then those templates will get instantiated as part of the DLL code if and only if you use the template in the dll's code.
    If you use the template in an application using the headers of the dll, then the template will be instantiated again in the code for your application. This could cause a link failure if the dll doesn't export the necessary members.

    You either accept the above as a given, and this is exactly what you want.
    OR. you export explicit instantiations of the templates from the DLL, and then assume only those instantiations are being used outside the DLL. In that case, the DLL will have code for the explicit instantiations (even if nobody ends up using them, just like any other function/class in the dll). And the exe can written to make use of the explicit instantiations from the DLL rather than reinstantiating them.

    Note that a template doesn't export anything. A template is a "shortcut to create some code" (not really, but maybe the analogy will help). a dll can only export actual instantiations of anything.



    as to the "is it going to be inline" question.
    This is up to the compiler and compiler settings. the template could be expanded "inline", or could be instantiated in the exe's code, with a call generated to it. It won't ever be a call to code in the dll, unless you explicitely call to a dll exported instance.

    Basically I'm trying to convert some code that was originally written for gcc. To boil it down to its simplest level the code builds 2 x DLLs. The source code for one DLL contains all the templates (let's call it DLLA) but the other one (DLLB) seems to use them. Here's what the code originally looked like:-
    a DLL doesn't "contain" templates. This is a wrong view to things just as saying that a "DLL contains headers".
    While the project of a dll may define headers. They could just as well not be part of the DLL project and just be located in a unrelated directory somewhere. headers don't generate code. (or you're doing it wrong)
    it's using the stuff from a header that may generate code. headers are just definitions, none of which formally end up as part of a DLL or EXE image. They're "stuff the compiler needs to validate the source".


    If I build the code without any modification, DLL B has no access to DLLA::Event. Nothing got exported from DLL A so they all come up as unresolved externals. If DLL::Event wasn't a template class this would be the quickest way to solve the problem:-
    from what I can see, that's because the template class only has function prototypes. Not the implementation of them. The implementation is usually "inlined" as part of the header, or listed outside the header after the class definition or it's part of a separate file you need to include as well as the header.

    if dll B says DLLA:Event<double> it doesn't know how to instantiate this because the implementation isn't available. It also can't call into DLLA because the specific Event<double> type memberfunctions aren't being exported from DLLA.


    class DLLA_API Event {
    OK, this is half the work. you now have told the compiler that it should locate the members of Event<something> in A.DLL and generate calls to it.
    but you now also need to explicitely instantiate the Event<type> members for each type you're expecting to be used.

    there's no way around it, either the consumer of the template instantiates, or the producer does (but then the producer needs to instantiate all types).


    assuming you want to export the types.
    create a source in dll A with the explicit template instantiations you want to export.
    Code:
    #include <event.h>
    // instantiate the types we want to support in this DLL for exporting
    template class DLLA:Event<int>; 
    template class DLLA:Event<int64>; 
    template class DLLA:Event<double>;

    DLLA_API Event (EventType...
    This isn't any different than the above, so I'm confused why this would work and the other wouldn't. Are you sure this actually does work ? an export definition at the class level is just "lasy mode" for doing the same on every member.
    Last edited by OReubens; October 21st, 2013 at 07:52 AM.

  11. #11
    John E is offline Elite Member Power Poster
    Join Date
    Apr 2001
    Location
    Manchester, England
    Posts
    4,835

    Re: Where does a template function get instantiated ?

    [Edit...] Read in conjunction with my next post

    Thanks for such a comprehensive response O'Reubens. BTW...

    Quote Originally Posted by OReubens View Post
    This isn't any different than the above, so I'm confused why this would work and the other wouldn't. Are you sure this actually does work ? an export definition at the class level is just "lasy mode" for doing the same on every member.
    Yes, I think you're right about that. I must have had the misfortune to pick one particular example that didn't work for some reason. All the others I've tried today have worked fine. Here's what I'm struggling to understand now...

    Code:
    /// Class that stores exceptions thrown from different threads
    class LIBAUDIOGRAPHER_API ThreaderException : public Exception
    {
      public:
    	template<typename T>
    	ThreaderException (T const & thrower, std::exception const & e)
    		: Exception (thrower,
    			boost::str ( boost::format
    			("\n\t- Dynamic type: %1%\n\t- what(): %2%")
    			% DebugUtils::demangled_name (e) % e.what() ))
    	{ }
    };
    If I have the above in a header file, as it's originally written (still getting compiled into DLL A) I find that DLL B reports that the symbol ThreadException is an unresolved external. So now I re-jig it to use the more conventional header file and cpp file - i.e.

    Code:
    HEADER FILE:
    
    /// Class that stores exceptions thrown from different threads
    class LIBAUDIOGRAPHER_API ThreaderException : public Exception
    {
    public:
    	template<typename T>
    	ThreaderException (T const & thrower, std::exception const & e);
    };
    Code:
    CPP FILE:
    
    template<typename T>
    ThreaderException::ThreaderException (T const & thrower, std::exception const & e)
    		: Exception (thrower,
    			boost::str ( boost::format
    			("\n\t- Dynamic type: %1%\n\t- what(): %2%")
    			% DebugUtils::demangled_name (e) % e.what() ))
    {
    }
    This time, DLL B links successfully to DLL A. This suggests that a concrete instantiation of ThreaderException gets exported from DLL A and DLL B can now find it.

    BUT...

    ThreadException objects are getting created (in DLL B) with different types of thrower. None of these were known at the time I compiled and linked DLL A. So if the compiler is using the template to create concrete instances of ThreadException (for all the different types of thrower) how can it do that when I build DLL A? At that point, it doesn't know how many types are going to be needed.

    There's still something about this that I'm failing to understand...

    [Edit...] further info in next post !
    Last edited by John E; October 21st, 2013 at 12:23 PM.
    "A problem well stated is a problem half solved.” - Charles F. Kettering

  12. #12
    John E is offline Elite Member Power Poster
    Join Date
    Apr 2001
    Location
    Manchester, England
    Posts
    4,835

    Re: Where does a template function get instantiated ?

    Quote Originally Posted by John E View Post
    There's still something about this that I'm failing to understand...
    I think I'm slowly getting this. Here's what I had at first....

    Code:
    /// Class that stores exceptions thrown from different threads
    class LIBAUDIOGRAPHER_API ThreaderException : public Exception
    {
      public:
    	template<typename T>
    	ThreaderException (T const & thrower, std::exception const & e)
    		: Exception (thrower,
    			boost::str ( boost::format
    			("\n\t- Dynamic type: %1%\n\t- what(): %2%")
    			% DebugUtils::demangled_name (e) % e.what() ))
    	{ }
    };
    If I simply take out the __declspec specifier (LIBAUDIOGRAPHER_API) I can then link DLL B to DLL A without any apparent issues.

    So.... am I right in thinking that:-

    a) A template class or function must be completely implemented in its header file (i.e. not with simple declarations which then get implemented in a separate source file) ?

    b) Template classes (and presumably functions) must not be declared as __declspec(dllexport) / (import) ?

    c) If a template function accesses static data it must do so via an accessor function ? (given that we don't know where precisely the template function will get instantiated)
    "A problem well stated is a problem half solved.” - Charles F. Kettering

  13. #13
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Where does a template function get instantiated ?

    Quote Originally Posted by John E View Post
    a) A template class or function must be completely implemented in its header file (i.e. not with simple declarations which then get implemented in a separate source file) ?
    That is the case without DLL's. Templates must be accompanied by its implementation within the same source module.

    The template implementation can be inlined within the template class, or outside the class in a separate file that gets #included directly after the template class. But regardless, the template and implementation must appear together in one unit.

    Regards,

    Paul McKenzie

  14. #14
    John E is offline Elite Member Power Poster
    Join Date
    Apr 2001
    Location
    Manchester, England
    Posts
    4,835

    Re: Where does a template function get instantiated ?

    Thanks Paul (and thanks everyone else!) I think this is finally starting to make some sense
    "A problem well stated is a problem half solved.” - Charles F. Kettering

  15. #15
    John E is offline Elite Member Power Poster
    Join Date
    Apr 2001
    Location
    Manchester, England
    Posts
    4,835

    Re: Where does a template function get instantiated ?

    One more question...

    If a template function needs to access static data, this should get done via an accessor function as previously discussed. But what about template classes? Is it considered bad practice (or at least very risky) for a template class to have static data members?
    "A problem well stated is a problem half solved.” - Charles F. Kettering

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured