Circular Class Template Friendship

T

Thomas Matthews

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
 
V

Victor Bazarov

Thomas Matthews said:
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.
 
R

Rob Williscroft

Thomas Matthews wrote in in
comp.lang.c++:
Hi,




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.
 
R

Rob Williscroft

Victor Bazarov wrote in in
comp.lang.c++:
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.
 
T

Thomas Matthews

Victor said:
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]
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
 
V

Victor Bazarov

Thomas said:
Victor said:
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
 
T

Thomas Matthews

Victor said:
Thomas said:
Victor said:
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
 
V

Victor Bazarov

Thomas said:
Victor said:
Thomas said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top