Can you use strings in template initialisation lists?

A

Adam Nielsen

Hi all,

I'm having a go at some template metaprogramming (well sort of) and I'm
stuck trying to figure out if it's possible to pass strings in template
parameter lists.

I am using the code below to convert values in a database into boolean.
(For example, the database stores the strings "ON" and "OFF" but I
want these read in to a variable as boolean true/false.)

The code below works for integers (converting predefined numbers into
true/false) but it doesn't seem to be possible to do the same with
strings. I realise of course I would need a specialisation for the
string comparison (using say strcmp() instead of ==), but for the moment
I'm mainly concerned with whether templates can be used like this or not.

Is this a limitation of C++, or do you just have to code it differently?

Thanks,
Adam.

-----------------------
#include <iostream>

template <typename TDatabase>
class ConvertType {
public:
template <TDatabase T, TDatabase F, bool D>
class ToBool {
public:
static bool toBool(const TDatabase &tTest) {
if (tTest == T) return true;
else if (tTest == F) return false;
else return D;
}
static TDatabase fromBool(bool bTest) {
return bTest ? T : F;
}
};
};

// This works fine
typedef ConvertType<int>::ToBool<0, 1, false> int_to_bool;

// This doesn't
typedef ConvertType<const char *>::ToBool<"ON", "OFF", false>
string_to_bool;

// And neither does this, even thought static const int does
static const char cTrue[] = "ON";
static const char cFalse[] = "OFF";
typedef ConvertType<const char *>::ToBool<cTrue, cFalse, false>
string_to_bool;

// Some example use
int main(void)
{
std::cout << std::boolalpha;

std::cout << "0 == " << int_to_bool::toBool(0) << std::endl;
std::cout << "1 == " << int_to_bool::toBool(1) << std::endl;

std::cout << "ON == " << string_to_bool::toBool("ON") << std::endl;
std::cout << "OFF == " << string_to_bool::toBool("OFF") << std::endl;

return 0;
}
 
T

Triple-DES

Hi all,

I'm having a go at some template metaprogramming (well sort of) and I'm
stuck trying to figure out if it's possible to pass strings in template
parameter lists.

I am using the code below to convert values in a database into boolean.
  (For example, the database stores the strings "ON" and "OFF" but I
want these read in to a variable as boolean true/false.)

The code below works for integers (converting predefined numbers into
true/false) but it doesn't seem to be possible to do the same with
strings.  I realise of course I would need a specialisation for the
string comparison (using say strcmp() instead of ==), but for the moment
I'm mainly concerned with whether templates can be used like this or not.

Is this a limitation of C++, or do you just have to code it differently?
[snip example]

It's possible, but it's cumbersome. The following would work:

// note that the objects need to have extern linkage
extern const char on[] = "ON";
extern const char off[] = "OFF";

template<const char *> struct C;

template<> struct C<off>
{
enum { value = false };
};
template<> struct C<on>
{
enum { value = true };
};

bool b = C<on>::value; // true
 
A

Adam Nielsen

It's possible, but it's cumbersome. The following would work:
// note that the objects need to have extern linkage
extern const char on[] = "ON";
extern const char off[] = "OFF";

template<const char *> struct C;

template<> struct C<off>
{
enum { value = false };
};
template<> struct C<on>
{
enum { value = true };
};

bool b = C<on>::value; // true

Thanks for your reply! Unfortunately I don't think this would end up
working in my situation, as I'd need to be able to pass in different
strings during the "checking" phase, i.e.

extern const char on[] = "ON";
template<> struct C<on>
{
enum { value = true };
};

const char d[] = "ON"; // value from database

bool b = C<d>::value;

And I don't think that would work :-( Thanks for the suggestion though!

Cheers,
Adam.
 
G

g3rc4n

I'm having a go at some template metaprogramming (well sort of) and I'm
stuck trying to figure out if it's possible to pass strings in template
parameter lists.
I am using the code below to convert values in a database into boolean.
  (For example, the database stores the strings "ON" and "OFF" but I
want these read in to a variable as boolean true/false.)
The code below works for integers (converting predefined numbers into
true/false) but it doesn't seem to be possible to do the same with
strings.  I realise of course I would need a specialisation for the
string comparison (using say strcmp() instead of ==), but for the moment
I'm mainly concerned with whether templates can be used like this or not.
Is this a limitation of C++, or do you just have to code it differently?

[snip example]

It's possible, but it's cumbersome. The following would work:

// note that the objects need to have extern linkage
extern const char on[] = "ON";
extern const char off[] = "OFF";

template<const char *> struct C;

template<> struct C<off>
{
  enum { value = false };};

template<> struct C<on>
{
  enum { value = true };

};

bool b = C<on>::value; // true

no because strings arn't static constants, and i don't think you can
take the address of a extern static string and anyway cos it's not
till linking i don't think. It's also not defined that conparing two
static strings by there ptr's is correct as compiles are allowed to
overlap strings to optimize storage.

anyway whats wrong with bool? assuming you want to use other certain
strings, as your only specializing your predefined static strings what
are you gaining versus an enum?

if you want transparency of types made with assigned strings in c++
you'll have to use polymorphism with a factory class
 
T

Triple-DES

The following would work:
// note that the objects need to have extern linkage
extern const char on[] = "ON";
extern const char off[] = "OFF";
template<const char *> struct C;
template<> struct C<off>
{
  enum { value = false };};
template<> struct C<on>
{
  enum { value = true };

bool b = C<on>::value; // true

no because strings arn't static constants, and i don't think you can

If by "string" you mean "string literal", first of all note that I do
not take the address of any string literal in my example. I am
initializing two arrays with string literals.
take the address of a extern static string and anyway cos it's not

"extern static string" is a contradiction in terms. If by "static" you
mean "with static storage duration", note that string literals have
static storage duration by definition.
till linking i don't think. It's also not defined that conparing two
static strings by there ptr's is correct as compiles are allowed to
overlap strings to optimize storage.

Again, I am not taking the address of any string literal in my
example.
anyway whats wrong with bool? assuming you want to use other certain
strings, as your only specializing your predefined static strings what
are you gaining versus an enum?

You could use a static const bool, but per C++03 that would require an
additional definition if it is used. For the full definition of "use",
see 3.2/2.

Consider:

void f(bool) {}
template<int I> void f() {}
struct C { static const bool value = true; };

int main()
{
f<C::value>(); // OK
f(C::value); // violates ODR

bool a = sizeof(char[C::value + 1]) - 1; // OK
bool b = C::value // violates ODR
}
 
A

Adam Nielsen

Is the string known at compile-time? If not, then templates are not the
right solution here.

The string isn't known at compile time, but as it is a parameter to a
template function (which takes a const char *) it should be okay. The
two main strings controlling the behaviour of the template class are
known at compile time, but I don't think I can use types because I will
need to compare them against the runtime string.

I don't need to specialise on a particular string (I don't really care
what the strings are) but I need to specialise on a particular type
(e.g. one instance of the template for ints, whatever they might be,
another instance for strings, etc.)

In other words, I need a way of doing this:

func = [5 => true, 10 => false]
assert(func(5) == true);
assert(func(10) == false);

func2 = [10 => true, 5 => false]
assert(func(5) == true);
assert(func(10) == false);

And likewise for strings:

func3 = ["on" => true, "off" => false]
assert(func("on") == true);
assert(func("off") == false);
const char *c = getDatabaseField();
std::cout << func(c); // the hard bit, using a runtime var

What I don't understand is why it works for int (see my original post),
but it doesn't work for const char *. Granted I can't compare pointers,
but I can use strcmp() to compare the data being pointed to.

Maybe I should cast my const char * to int to make it work ;-)

Cheers,
Adam.
 
A

Adam Nielsen

Whoops, correction - logic should be swapped in that second example:

func2 = [10 => true, 5 => false]
assert(func(5) == false);
assert(func(10) == true);

Also worth pointing out that I'd like to use other strings too:

func4 = ["hello" => true, "goodbye" => false]
assert(func("hello") == true);
assert(func("goodbye ") == false);
const char *c = getDatabaseField();
std::cout << func(c); // if c is hello, should print true
 
D

Daniel Pitts

Adam said:
The string isn't known at compile time, but as it is a parameter to a
template function (which takes a const char *) it should be okay.
Unfortunately, that's wrong. Templates are all evaluated at compile
time. You aren't "passing" a value in at runtime, you're instantiating
a template at compile time.

You'll have to think of some other approach. Possibly using a map, or
passing the const char * in a constructor instead.
 
A

Adam Nielsen

The string isn't known at compile time, but as it is a parameter to a
Unfortunately, that's wrong. Templates are all evaluated at compile
time. You aren't "passing" a value in at runtime, you're instantiating
a template at compile time.

That's true, but I seem to be unable to instantiate a template at
compile time with a const char * parameter. The algorithm works fine
using an int parameter, but not char *. I don't understand why a
generic template function can work with an int parameter but not a const
char * - I assumed they were both the same (i.e. numbers, and it's up to
the code to do something sensible with those numbers at runtime.)

Cheers,
Adam.
 
K

Kai-Uwe Bux

Adam said:
That's true, but I seem to be unable to instantiate a template at
compile time with a const char * parameter. The algorithm works fine
using an int parameter, but not char *. I don't understand why a
generic template function can work with an int parameter but not a const
char * - I assumed they were both the same (i.e. numbers, and it's up to
the code to do something sensible with those numbers at runtime.)

What are you trying? The following is a hello world program:

#include <iostream>
#include <ostream>

template < char const * name >
struct X {

static
std::eek:stream & print ( std::eek:stream & ostr ) {
return ( ostr << name );
}

};

extern char const greeting [] = "hello world!\n";

int main ( void ) {
X< greeting >::print( std::cout );
}

If I take out the "extern", I get errors. Presumably, the extern forces the
compiler to make up its mind where to put the string and then template
instantiation can proceed (I am too lazy to look up details now, but I have
the vague recollection that extern was needed for pointers for some
reason).


Best

Kai-Uwe Bux
 
A

Adam Nielsen

extern char const greeting [] = "hello world!\n";
If I take out the "extern", I get errors. Presumably, the extern forces the
compiler to make up its mind where to put the string and then template
instantiation can proceed (I am too lazy to look up details now, but I have
the vague recollection that extern was needed for pointers for some
reason).

Ha, that got it working! All I had to do was to declare my string
constants as extern and it works fine. Here's an updated example (from
my first post) with a specialisation for const char *.

Thanks everyone for all the suggestions, and thanks Kai-Uwe for the
solution!

Cheers,
Adam.

--

#include <iostream>
#include <string.h>

template <typename TDatabase>
class ConvertType {
public:
template <TDatabase T, TDatabase F, bool D>
class ToBool {
public:
static bool toBool(const TDatabase &tTest) {
if (tTest == T) return true;
else if (tTest == F) return false;
else return D;
}
static TDatabase fromBool(bool bTest) {
return bTest ? T : F;
}
};
};

template <>
class ConvertType<const char *> {
public:
template <const char *T, const char *F, bool D>
class ToBool {
public:
static bool toBool(const char *tTest) {
if (strcmp(tTest, T) == 0) return true;
else if (strcmp(tTest, F) == 0) return false;
else return D;
}
static const char *fromBool(bool bTest) {
return bTest ? T : F;
}
};
};

// This works fine
typedef ConvertType<int>::ToBool<0, 1, false> int_to_bool;

// Works now with "extern"
extern const char cTrue[] = "ON";
extern const char cFalse[] = "OFF";
typedef ConvertType<const char *>::ToBool<cTrue, cFalse, false>
string_to_bool;

// Some example use
int main(void)
{
std::cout << std::boolalpha;

std::cout << "0 should be true: " << int_to_bool::toBool(0)
<< std::endl;
std::cout << "1 should be false: " << int_to_bool::toBool(1)
<< std::endl;

std::cout << "ON should be true: " << string_to_bool::toBool("ON")
<< std::endl;
std::cout << "OFF should be false: " << string_to_bool::toBool("OFF")
<< std::endl;

return 0;
}
 
J

James Kanze

Adam Nielsen wrote:

[...]
What are you trying? The following is a hello world program:
#include <iostream>
#include <ostream>
template < char const * name >
struct X {

static
std::eek:stream & print ( std::eek:stream & ostr ) {
return ( ostr << name );
}
};
extern char const greeting [] = "hello world!\n";
int main ( void ) {
X< greeting >::print( std::cout );
}
If I take out the "extern", I get errors. Presumably, the
extern forces the compiler to make up its mind where to put
the string and then template instantiation can proceed (I am
too lazy to look up details now, but I have the vague
recollection that extern was needed for pointers for some
reason).

For pointer (and reference) type arguments, often. The standard
requires the pointed to object to have external linkage; if the
variable is const, it has internal linkage by default, so you
have to add the extern to force external linkage. (I get hit
with this all the time: I define the variables in an unnamed
namespace, so that they will have external linkage, forgetting
that const defaults to internal, and get errors.)
 
T

Triple-DES

extern char const greeting [] = "hello world!\n";
If I take out the "extern", I get errors. Presumably, the extern forces the
compiler to make up its mind where to put the string and then template
instantiation can proceed (I am too lazy to look up details now, but I have
the vague recollection that extern was needed for pointers for some
reason).

Ha, that got it working!  All I had to do was to declare my string
constants as extern and it works fine.  Here's an updated example (from
my first post) with a specialisation for const char *.

Hey, great that you got it working. I'd just like to point out that I
mentioned this already in my first reply :)

(...)
// note that the objects need to have extern linkage
extern const char on[] = "ON";
extern const char off[] = "OFF";
(...)

[snip working example]
 
A

Adam Nielsen

Hey, great that you got it working. I'd just like to point out that I
mentioned this already in my first reply :)

Oh sorry, indeed you did! Once I saw the templates wouldn't work like
that in my code I didn't notice the extern :-/

Cheers,
Adam.
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top