Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Circular Class Template Friendship

Reply
Thread Tools

Circular Class Template Friendship

 
 
Thomas Matthews
Guest
Posts: n/a
 
      07-24-2004
Hi,

I am converting my table and record classes into templates.
My issue is the syntax of declaring a friend class within
the template. I have searched the C++ FAQ Lite (web),
the C++ newsgroups, "Thinking In C++" to no avail.


Background
----------
My table is a collection of <integer, string> pairs, in
which the string is a fixed width that is specialized.
One specialization of the table may have a 32 length
string and another 64. The table is also a Singleton.

The record class is of the form <integer, string>.
The integer portion, the primary key, is hidden from
public usage, so that the class behaves like a string.
When the class is written as a field of a record, the
integer portion will be written out.

Part of the interface is for the table to be a friend
of the record. This allows the table to set the primary
key without giving access or knowledge of the primary
key to the general public.


The Code
---------
Here is my code:
// File Name_Id_Table.hpp

template <typename Record_Class,
const char * TABLE_NAME>
class Name_Id_Table
{
//...
public:
void load_from_table(Record_Class& rc)
{
rc.id = get_id_from_table();
rc.name = get_name_from_table();
}
};

// File Name_Id.hpp
#include <string>
using std::string

template <int MAX_STRING_WIDTH>
class Name_Id
{
// ...

/* The following line is what I need help with */
template <> friend class<Name_ID, ????> Name_Id_Table;

private:
int id;
string name;
};


// File main.cpp
#include "Name_Id_Table.hpp"
#include "Name_Id.hpp"

const char * TITLE_TABLE_NAME = "Titles";

/* Here is another issue I'm having problems with.
* I want to declare the types but am having syntax
* issues.
*/
typedef Name_Id<64> Title;
typedef Name_Id_Table<Title, TITLE_TABLE_NAME> Title_Table;

int main(void)
{
Title t;
Title_Table table;

table.load_from_table(t);

return EXIT_SUCCESS;
}



In the database, I will have three Name-ID tables:
Title, Author, Publisher. The string widths will
differ and they will have different table names.
Otherwise they have the same functionality. Each
table is a singleton; the Title table will not
contain author or publisher entries. Similarly
with Author and Publisher tables.


--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
http://www.sgi.com/tech/stl -- Standard Template Library

 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      07-24-2004
"Thomas Matthews" <(E-Mail Removed)> wrote...
> I am converting my table and record classes into templates.
> My issue is the syntax of declaring a friend class within
> the template. I have searched the C++ FAQ Lite (web),
> the C++ newsgroups, "Thinking In C++" to no avail.
>
>
> Background
> ----------
> My table is a collection of <integer, string> pairs, in
> which the string is a fixed width that is specialized.
> One specialization of the table may have a 32 length
> string and another 64. The table is also a Singleton.
>
> The record class is of the form <integer, string>.
> The integer portion, the primary key, is hidden from
> public usage, so that the class behaves like a string.
> When the class is written as a field of a record, the
> integer portion will be written out.
>
> Part of the interface is for the table to be a friend
> of the record. This allows the table to set the primary
> key without giving access or knowledge of the primary
> key to the general public.
>
>
> The Code
> ---------
> Here is my code:
> // File Name_Id_Table.hpp
>
> template <typename Record_Class,
> const char * TABLE_NAME>
> class Name_Id_Table
> {
> //...
> public:
> void load_from_table(Record_Class& rc)
> {
> rc.id = get_id_from_table();
> rc.name = get_name_from_table();
> }
> };
>
> // File Name_Id.hpp
> #include <string>
> using std::string


Ugh! Yuck!!! Never put a using declaration into a global scope in
a header. NEVER! There is no reason for it to be there. If you
are so inclined to save some typing and use 'string' instead of
'std::string' in your class definition, put the 'using' there, inside
that class definition. Hide your 'using's as deep as possible.

>
> template <int MAX_STRING_WIDTH>
> class Name_Id
> {
> // ...
>
> /* The following line is what I need help with */
> template <> friend class<Name_ID, ????> Name_Id_Table;


What are you trying to accomplish? All possible Name_Id_Table
instantiations with the same Name_ID should be friends or only
the one that has a particular TABLE_NAME?

At this point, it is still possible to have Name_Id_Table template
specialised on the same Name_Id class, but with different table
names. There is nothing in your code that prevents that.

So, do you want a particular fully specialised Name_Id_Table to
be a friend? Then you have to give the address of a constant
character here. Which one? You decide. The easiest solution
would be to give Name_Id an extra argument and pass it along to
the friend class declaration.

>
> private:
> int id;
> string name;
> };
>
>
> // File main.cpp
> #include "Name_Id_Table.hpp"
> #include "Name_Id.hpp"
>
> const char * TITLE_TABLE_NAME = "Titles";


No, that has to be 'extern'.

>
> /* Here is another issue I'm having problems with.
> * I want to declare the types but am having syntax
> * issues.


WHAT syntax issues?

> */
> typedef Name_Id<64> Title;
> typedef Name_Id_Table<Title, TITLE_TABLE_NAME> Title_Table;
>
> int main(void)
> {
> Title t;
> Title_Table table;
>
> table.load_from_table(t);
>
> return EXIT_SUCCESS;
> }
>
>
>
> In the database, I will have three Name-ID tables:
> Title, Author, Publisher. The string widths will
> differ and they will have different table names.


You can create a templated "table of widths" and only worry
about passing the name to your 'Name_Id' template and to the
'Name_Id_Table' template:
---------------------------------------------------------
template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth }; };
template<typename T, const char* name> class Name_Id_Table;

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id, name>;
};

template<typename T, const char* name> class Name_Id_Table
{
public:
int bar(T& t) { return t.foo(); }
};

extern const char TITLE[] = "Title";
extern const char AUTHOR[] = "Author";
extern const char PUBLISHER[] = "Publisher";

template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96 }; };

typedef Name_Id<TITLE> id_Title;
typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;

int main()
{
id_Title idt;
id_Title_Table idtt;

idtt.bar(idt);

return 0;
}

> Otherwise they have the same functionality. Each
> table is a singleton; the Title table will not
> contain author or publisher entries. Similarly
> with Author and Publisher tables.


If you tie them by the name only, you have a chance, I believe.


 
Reply With Quote
 
 
 
 
Rob Williscroft
Guest
Posts: n/a
 
      07-24-2004
Thomas Matthews wrote in news:(E-Mail Removed) in
comp.lang.c++:

>
> Hi,
>




#include <cstdlib>
//^^^^^^^^^^^^^^^^ EXIT_SUCCESS

> template <typename Record_Class,
> const char * TABLE_NAME>
> class Name_Id_Table
> {
> public:
> void load_from_table(Record_Class& rc)
> {
> // [snipped]
> }
> };
>
> // File Name_Id.hpp
> #include <string>


using std::string;
// ^^^^^^missing ;

> template <int MAX_STRING_WIDTH>
> class Name_Id
> {
> // ...
>
> /* The following line is what I need help with */

template < typename, const char * >
friend class Name_Id_Table;

/* ^^^^^^^^^^^^^^^^^^^^^^^^

friend-ship needs to be the class template or an explicit
specialization, partial-specialization's not allowed.
*/

> private:
> int id;
> string name;
> };
>
>

extern char const TITLE_TABLE_NAME[] = "Titles";
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

>
> /* Here is another issue I'm having problems with.
> * I want to declare the types but am having syntax
> * issues.


See Above ^^^^^^^

> */
> typedef Name_Id<64> Title;
> typedef Name_Id_Table<Title, TITLE_TABLE_NAME> Title_Table;
>
> int main(void)
> {
> Title t;
> Title_Table table;
>
> table.load_from_table(t);
>
> return EXIT_SUCCESS;
> }



BTW - Most people prefer lowercase or MixedCase for non-macro
identifiers, for example TABLE_NAME and TITLE_TABLE_NAME above.

HTH.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
 
Reply With Quote
 
Rob Williscroft
Guest
Posts: n/a
 
      07-24-2004
Victor Bazarov wrote in news:HzwMc.21062$eM2.20260@attbi_s51 in
comp.lang.c++:

>> // File Name_Id.hpp
>> #include <string>
>> using std::string

>
> Ugh! Yuck!!! Never put a using declaration into a global scope in
> a header. NEVER! There is no reason for it to be there.


Can't disagree with that.

> If you
> are so inclined to save some typing and use 'string' instead of
> 'std::string' in your class definition, put the 'using' there, inside
> that class definition. Hide your 'using's as deep as possible.
>


#include <string>

struct x
{
using std::string;
};

test.cpp(5) : error C2886: 'std::string' : symbol cannot be used in
a member using-declaration

A typedef does work though:

#include <string>

struct x
{
typedef std::string string;
};

Rob.
--
http://www.victim-prime.dsl.pipex.com/
 
Reply With Quote
 
Thomas Matthews
Guest
Posts: n/a
 
      07-26-2004
Victor Bazarov wrote:
> "Thomas Matthews" <(E-Mail Removed)> wrote...
>
>>// File Name_Id.hpp
>>#include <string>
>>using std::string

>
>
> Ugh! Yuck!!! Never put a using declaration into a global scope in
> a header. NEVER! There is no reason for it to be there. If you
> are so inclined to save some typing and use 'string' instead of
> 'std::string' in your class definition, put the 'using' there, inside
> that class definition. Hide your 'using's as deep as possible.
>
>

I've decided to move it out of the class because it doesn't
compile when inside the class. See Rob's response to your article.

[snip]
>>In the database, I will have three Name-ID tables:
>>Title, Author, Publisher. The string widths will
>>differ and they will have different table names.

>
>
> You can create a templated "table of widths" and only worry
> about passing the name to your 'Name_Id' template and to the
> 'Name_Id_Table' template:
> ---------------------------------------------------------
> template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth }; };
> template<typename T, const char* name> class Name_Id_Table;
>
> template<const char* name> class Name_Id {
> enum { MAX_STRING_WIDTH =
> Name_Id_Aux_Info<name>::MaxWidth };
>
> int foo() { return 42; } // private
> friend class Name_Id_Table<Name_Id, name>;

At this point, my compiler, Borland Builder 6.0, gives me this
error:
E2299 Cannot generate template specialization from
'Name_Id_Table<T, name>'

Although Gnu G++ 3.3.1 compiles without any warnings or errors.

> };
>
> template<typename T, const char* name> class Name_Id_Table
> {
> public:
> int bar(T& t) { return t.foo(); }
> };
>
> extern const char TITLE[] = "Title";
> extern const char AUTHOR[] = "Author";
> extern const char PUBLISHER[] = "Publisher";
>
> template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
> template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
> template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96 }; };
>
> typedef Name_Id<TITLE> id_Title;
> typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;
>
> int main()
> {
> id_Title idt;
> id_Title_Table idtt;
>
> idtt.bar(idt);
>
> return 0;
> }


Is Gnu correct in this case or Borland?
{Generates same error with BCC32 version 5.6}


--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book

 
Reply With Quote
 
Victor Bazarov
Guest
Posts: n/a
 
      07-26-2004
Thomas Matthews wrote:
> Victor Bazarov wrote:
>> You can create a templated "table of widths" and only worry
>> about passing the name to your 'Name_Id' template and to the
>> 'Name_Id_Table' template:
>> ---------------------------------------------------------
>> template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth
>> }; };
>> template<typename T, const char* name> class Name_Id_Table;
>>
>> template<const char* name> class Name_Id {
>> enum { MAX_STRING_WIDTH =
>> Name_Id_Aux_Info<name>::MaxWidth };
>>
>> int foo() { return 42; } // private
>> friend class Name_Id_Table<Name_Id, name>;

>
> At this point, my compiler, Borland Builder 6.0, gives me this
> error:
> E2299 Cannot generate template specialization from
> 'Name_Id_Table<T, name>'
>
> Although Gnu G++ 3.3.1 compiles without any warnings or errors.
>
>> };
>>
>> template<typename T, const char* name> class Name_Id_Table
>> {
>> public:
>> int bar(T& t) { return t.foo(); }
>> };
>>
>> extern const char TITLE[] = "Title";
>> extern const char AUTHOR[] = "Author";
>> extern const char PUBLISHER[] = "Publisher";
>>
>> template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
>> template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
>> template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96
>> }; };
>>
>> typedef Name_Id<TITLE> id_Title;
>> typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;
>>
>> int main()
>> {
>> id_Title idt;
>> id_Title_Table idtt;
>>
>> idtt.bar(idt);
>>
>> return 0;
>> }

>
>
> Is Gnu correct in this case or Borland?
> {Generates same error with BCC32 version 5.6}


The code I posted compiles fine with Comeau online and with VC++ v7.1
which kind of suggests that the message Borland compiler emits is not
an indication of the error in the code but rather the admission of
BCBuilder's impotency.

Victor
 
Reply With Quote
 
Thomas Matthews
Guest
Posts: n/a
 
      07-28-2004
Victor Bazarov wrote:

> Thomas Matthews wrote:
>
>> Victor Bazarov wrote:
>>
>>> You can create a templated "table of widths" and only worry
>>> about passing the name to your 'Name_Id' template and to the
>>> 'Name_Id_Table' template:
>>> ---------------------------------------------------------
>>> template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth
>>> }; };
>>> template<typename T, const char* name> class Name_Id_Table;
>>>
>>> template<const char* name> class Name_Id {
>>> enum { MAX_STRING_WIDTH =
>>> Name_Id_Aux_Info<name>::MaxWidth };
>>>
>>> int foo() { return 42; } // private
>>> friend class Name_Id_Table<Name_Id, name>;

>>
>>
>> At this point, my compiler, Borland Builder 6.0, gives me this
>> error:
>> E2299 Cannot generate template specialization from
>> 'Name_Id_Table<T, name>'
>>
>> Although Gnu G++ 3.3.1 compiles without any warnings or errors.
>>
>>> };
>>>
>>> template<typename T, const char* name> class Name_Id_Table
>>> {
>>> public:
>>> int bar(T& t) { return t.foo(); }
>>> };
>>>
>>> extern const char TITLE[] = "Title";
>>> extern const char AUTHOR[] = "Author";
>>> extern const char PUBLISHER[] = "Publisher";
>>>
>>> template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
>>> template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
>>> template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96
>>> }; };
>>>
>>> typedef Name_Id<TITLE> id_Title;
>>> typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;
>>>
>>> int main()
>>> {
>>> id_Title idt;
>>> id_Title_Table idtt;
>>>
>>> idtt.bar(idt);
>>>
>>> return 0;
>>> }

>>
>>
>>
>> Is Gnu correct in this case or Borland?
>> {Generates same error with BCC32 version 5.6}

>
>
> The code I posted compiles fine with Comeau online and with VC++ v7.1
> which kind of suggests that the message Borland compiler emits is not
> an indication of the error in the code but rather the admission of
> BCBuilder's impotency.
>
> Victor


I posted your program in a Borland newsgroup and they suggested
the following modification:

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id<name>, name>;
/* ^^^^^^ */

This makes sense, but I don't know why the other compilers
don't require it.

When I make the change, the program compiles fine with
the Borland compiler.

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.comeaucomputing.com/learn/faq/
Other sites:
http://www.josuttis.com -- C++ STL Library book

 
Reply With Quote
 
Victor Bazarov
Guest
Posts: n/a
 
      07-28-2004
Thomas Matthews wrote:
> Victor Bazarov wrote:
>
>> Thomas Matthews wrote:
>>
>>> Victor Bazarov wrote:
>>>
>>>> You can create a templated "table of widths" and only worry
>>>> about passing the name to your 'Name_Id' template and to the
>>>> 'Name_Id_Table' template:
>>>> ---------------------------------------------------------
>>>> template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth
>>>> }; };
>>>> template<typename T, const char* name> class Name_Id_Table;
>>>>
>>>> template<const char* name> class Name_Id {
>>>> enum { MAX_STRING_WIDTH =
>>>> Name_Id_Aux_Info<name>::MaxWidth };
>>>>
>>>> int foo() { return 42; } // private
>>>> friend class Name_Id_Table<Name_Id, name>;
>>>
>>>
>>>
>>> At this point, my compiler, Borland Builder 6.0, gives me this
>>> error:
>>> E2299 Cannot generate template specialization from
>>> 'Name_Id_Table<T, name>'
>>>
>>> Although Gnu G++ 3.3.1 compiles without any warnings or errors.
>>>
>>>> };
>>>>
>>>> template<typename T, const char* name> class Name_Id_Table
>>>> {
>>>> public:
>>>> int bar(T& t) { return t.foo(); }
>>>> };
>>>>
>>>> extern const char TITLE[] = "Title";
>>>> extern const char AUTHOR[] = "Author";
>>>> extern const char PUBLISHER[] = "Publisher";
>>>>
>>>> template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
>>>> template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
>>>> template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96
>>>> }; };
>>>>
>>>> typedef Name_Id<TITLE> id_Title;
>>>> typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;
>>>>
>>>> int main()
>>>> {
>>>> id_Title idt;
>>>> id_Title_Table idtt;
>>>>
>>>> idtt.bar(idt);
>>>>
>>>> return 0;
>>>> }
>>>
>>>
>>>
>>>
>>> Is Gnu correct in this case or Borland?
>>> {Generates same error with BCC32 version 5.6}

>>
>>
>>
>> The code I posted compiles fine with Comeau online and with VC++ v7.1
>> which kind of suggests that the message Borland compiler emits is not
>> an indication of the error in the code but rather the admission of
>> BCBuilder's impotency.
>>
>> Victor

>
>
> I posted your program in a Borland newsgroup and they suggested
> the following modification:
>
> template<const char* name> class Name_Id {
> enum { MAX_STRING_WIDTH =
> Name_Id_Aux_Info<name>::MaxWidth };
>
> int foo() { return 42; } // private
> friend class Name_Id_Table<Name_Id<name>, name>;
> /* ^^^^^^ */
>
> This makes sense, but I don't know why the other compilers
> don't require it.


You should ask Borland why they made it so that _their_ compiler _does_
require it.

The 'Name_Id' is the name of the template that is injected into the
class template scope. Every time you [try to] instantiate Name_Id,
it should become a concrete class, which then should specify which
specialisation of Name_Id_Table is used without the need to specify
the Name_Id's template argument explicitly, IMO.

[I am too lazy to look for a quote from the Standard.]

>
> When I make the change, the program compiles fine with
> the Borland compiler.
>


I'm glad you got it resolved.

Victor
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Is it standard that inner class inherits the outer class' friendship? Qi C++ 4 05-01-2011 04:57 AM
Declaring a template class with two template params a friend in anon-template class A L C++ 1 08-25-2010 07:25 AM
A parameterized class (i.e. template class / class template) is not a class? christopher diggins C++ 16 05-04-2005 12:26 AM
How to declare friendship-ness to all inherited class? Kai Wu C++ 2 03-02-2005 12:38 AM
Semi-circular definitions (plus circular references) Kiuhnm C++ 16 01-03-2005 03:49 AM



Advertisments