c++: Passing const int to template function - c++

I am using the rapidxml lib.
It defines a function to parse files in this way:
template<int Flags>
void parse(Ch *text)
The lib provides const int flags for example:
const int parse_declaration_node = 0x20;
So I created a pointer to a static int in my class:
const int * parser_mode;
And in the class constructor I assigned it its value:
parser_mode = &rapidxml::parse_declaration_node;
Then when I try to use this const int * as template argument to the parse function:
tree->parse<parser_mode>(file->data());
I get this error message:
error: ‘GpxSectionData::parser_mode’ cannot appear in a
constant-expression
This rest of the statement seems correct since:
tree->parse<0>(file->data());
doesn't produce compilation error...
Could you please tell me what I am missing here?
Thank you!
Thanks to the explanations below I will probably define it out of the class:
So I think this is:
class Myclass {
static const int parser_mode;
[...]
}
static const int Myclass::parser_mode = rapidxml::parse_declaration_node;

template<int Flags> void parse(Ch *text) ... const int * parser_mode;
Your template takes an int as a template parameter, but you are passing it an int*. The types int and int* are not the same.
Try tree->parse<rapidxml::parse_declaration_node>(file->data());

You cannot use variable for value of template parameter.
Instead you can add Flags template parameter to your class
template<int Flags>
class CClass {
//...
};
And set Flags parameter for class instance
CClass<rapidxml::parse_declaration_node> obj;

Related

extern declared template specialized function not found

i'm trying to implement a clone of the json serialization library nlohmann::json as a learning experience, and i'm having trouble with the interface for user defined (json<->User type) conversion.
Basically i want the user to be able to overload two function: to_json(json&, const Type&) and from_json(const json&, Type&). Then the library will use overload resolution to call theses function in the templated operator= and one argument constructor.
It works fine when i'm just defining theses function directly but when i try to make a template definition for multiple types (in this example the class S) the linker can't find the definition.
I've tried to explicitly instantiate the function for individual instances of the templated class although i would prefer avoiding having to do that in the final product.
I'm guessing it has to do with the fact that templated function don't have the same signature than free function, but i don't see what i can do to make it work. What am i missing ? I also couldn't find result on google so is it a documented pattern or an anti pattern ?
Thanks you. Below i tried to minimize my problem in one short example.
Class.hpp
#pragma once
#include <cstdio>
template<size_t i>
class S {
size_t n = i;
};
template<size_t i>
void g(const S<i>& s) {
printf("S<%u>\n", i);
}
Class.cpp
#include "Class.hpp"
template void g<10>(const S<10>&); // <-- Even with explicitly instanciation
void g(const bool& b) {
printf("%s\n", b ? "true" : "false");
}
main.cpp
#include "Class.hpp"
template<typename T>
void f(T t) {
extern void g(const T&);
g(t);
}
int main(int, char**) {
S<10> s;
//f(s); <-- linker error: void g(class S<10> const &) not found.
f(false);
}
The name lookup for g in g(t) call stops as soon as it finds extern void g(const T&); declaration; it never sees the declaration of the function template. So the compiler generates a call to a regular non-template function named g taking const S<10>&. But no such function is defined in your program - hence linker error.

Visual C++ cares about the array size in function arguments. Is that correct?

When I try compiling the following (broken) code produced by CUDA's cudafe++ tool, Visual Studio throws error C2244. Is that correct behavior? GCC does not seem to care about the signature mismatch.
Code:
template<int Size>
class MyClass {
public:
MyClass(const int data[Size]);
};
template<int Size>
MyClass<Size> ::MyClass(const int data[]) {}
void func(MyClass<4> input) {}
Output:
test2.cpp(9) : error C2244: 'MyClass<Size>::MyClass' : unable to match function definition to an existing declaration
test2.cpp(5) : see declaration of 'MyClass<Size>::MyClass'
definition
'MyClass<Size>::MyClass(const int [])'
existing declarations
'MyClass<Size>::MyClass(const int [Size])'
I am pretty sure it is not correct.
int foo(const int []);
int foo(const int [4]);
int foo(const int *);
should all declare the same function. Having said that, what you may want is:
template<int Size>
class MyClass {
public:
MyClass(const int (&data)[Size]);
};
template<int Size>
MyClass<Size> ::MyClass(const int (&data)[Size]) {}
which will only accept an array of the right size.

Can a constexpr class template literal type be parameterized on a non-literal type?

I am playing around with an ORM design using gcc 4.9.2. I have a class of 2 members:
class Staff : public Db::TableBase<Staff> {
public:
long int staffId_;
std::string nickname_;
}
(TableBase is a class I use to pull in several static functions and subclass declarations into class scope. It has no members or methods, only static functions and subclass declarations.)
I have have a ColumnObject for each member:
static constexpr auto colTest1=Db::detail::ColumnObject<Staff,long int>
(&Staff::staffId_,"staff_id",Db::BigSerial | Db::Default,Db::Transport::TEXT);
static constexpr auto colTest2=Db::detail::ColumnObject<Staff, std::basic_string<char> >
(&Staff::nickname_,"nick_name",Db::NotNull,Db::Transport::TEXT);
The first statement compiles, the second does not:
~/git/WebSchedule2/src/common/ORM/Staff.hpp:110:152: error: ‘const Db::detail::ColumnObject<Staff, std::basic_string<char> >{&Staff::nickname_, ((const char*)"nick_name"), 1, (Db::Transport)0, -1}’ is not a constant expression
static constexpr auto colTest2=Db::detail::ColumnObject<Staff, std::basic_string<char> >(&Staff::nickname_,"nick_name",Db::NotNull,Db::Transport::TEXT);
You might say that the reason the second statement doesn't compile is because the type dependency on std::string makes it a non-literal. However, I don't actually use a string in the definition of ColumnObject, I only use the type information.The definition of ColumnObject:
template<typename Derived,typename MemberType>
class ColumnObject {
public:
MemberType Derived::*memberPtr_;
char const *columnName_;
int const traits_;
Db::Transport transport_;
int columnNumber_;
constexpr ColumnObject()
: memberPtr_(nullptr), columnName_("UNITIALIZED COLUMN"), traits_(-1),
transport_(Db::Transport::TEXT), columnNumber_(-1) { }
constexpr ColumnObject(MemberType Derived::*memberPtr, char const *columnName,
int const traits, Db::Transport transport)
: memberPtr_(memberPtr), columnName_(columnName), traits_(traits),
transport_(transport), columnNumber_(-1) { }
}
Any ideas how I can work around this?
(Edit: I am including a complete example below. The problem seems to be using static constexprs.)
// g++ -Wall -Wextra -pedantic -std=c++14 -c foo.cpp
#include <string>
template<typename Derived,typename MemberType>
class ColumnObject;
template<typename Derived,typename MemberType>
class ColumnObject {
public:
MemberType Derived::*memberPtr_;
char const *columnName_;
constexpr ColumnObject()
: memberPtr_(nullptr), columnName_("UNITIALIZED COLUMN") { }
constexpr ColumnObject(MemberType Derived::*memberPtr, const char *columnName)
: memberPtr_(memberPtr), columnName_(columnName) { }
};
class Staff {
public:
long int staffId_;
std::string nickname_;
Staff(){
staffId_=0;
nickname_="";
}
Staff(long int snid, std::string nname)
: staffId_(snid), nickname_(nname)
{
};
static constexpr auto colTest1=ColumnObject<Staff,long int>(&Staff::staffId_,"staff_id");
static constexpr auto colTest2=ColumnObject<Staff, std::string >(&Staff::nickname_,"nick_name");
};
int main(int argc, char *argv[]){
constexpr auto colTest3=ColumnObject<Staff,long int>(&Staff::staffId_,"staff_id");
constexpr auto colTest4=ColumnObject<Staff, std::string >(&Staff::nickname_,"nick_name");
}
working fine here, on clang :) -> coliru.stacked-crooked.com/a/b9a3102e4849b0d0 – pepper_chico
This now works in RedHat gcc 5.3.1-2. – user2352497

Using static const + const as array bound

I'm doing something like this
Class.hpp:
class Class {
private:
static const unsigned int arraySize;
int ar[arraySize+2];
};
Class.cpp:
#include <Class.hpp>
const unsigned int arraySize = 384;
The compiler (q++, a c++ compiler for the QNX OS based on g++) gives me error: array bound is not an integer constant while compiling a unit including Class.hpp (not while compiling Class.cpp).
Why isn't that working? I know that a static const member can be used as an array bound, guaranteed by the C++ standard (see this anwser). But why doesn't the compiler see the result of static const + const as a constant?
This is good code which should have been accepted by the compiler:
class Class {
const static int arraySize = 384;
int ar[arraySize+2];
};
and if it isn't, your compiler is broken.
However, if you move actual constant out of the header file to selected translation unit, that invalidates the code.
// Class.h
class Class {
const static int arraySize;
int ar[arraySize+2]; // ERROR
};
// Class.cpp
const int Class::arraySize = 384;
This is because the size of your Class object cannot be determined at compile time from the data available in the header alone. This is not exactly right reason, but reasoning along these lines helps to understand compilation errors such as this.
To avoid making such mistakes, you can replace static const int with an enum, e.g.
class Class {
enum { arraySize = 384 };
int ar[arraySize+2];
};
I'm surprised this actually compiles on gcc, as a comment says. Since the 384 isn't in the header file, the size of the Class is not known to other compilation units. It might not matter in some compilation units depending on how/if they are using Class, but I can't imagine this compiling:
// this is a source file called, say, blah.cpp
#include <Class.hpp>
void someFunc()
{
void *mem = malloc(sizeof(Class)); // size is not known, so this can't compile
// do something with mem
}
You need to have in your .hpp:
class Class {
private:
static const unsigned int arraySize = 384;
int ar[arraySize+2];
};
.. as it is in the OP that you link to here.

Inheriting from template class

#include <iostream>
#include <math.h>
using namespace std;
template<template<short,short,short,int> class Derived>
struct AllocFactor_Core
{
private:
static long double factor_;
static long double calcFactor_(const short mantissa, const short exponent,const short base)
{
return mantissa * ::pow(static_cast<long double>(base),exponent);
}
public:
static const long double getFactor()
{
return factor_;
}
void setFactor(const short mantissa,const short exponent,const short base)
{
factor_ = calcFactor_(mantissa,exponent,base);
}
void setFactor(const long double factor)
{
factor_ = factor;
}
};
template<short Mantissa, short Exponent, short Base = 10, int Tag = 0>
struct AllocFactorScientific : private AllocFactor_Core<AllocFactorScientific>
{
static short base_;
using AllocFactor_Core<AllocFactorScientific<Mantissa,Exponent,Base,Tag>>::getFactor;
//I'm getting an error here saying: error: type/value mismatch at argument 1 in template
//parameter list for 'template<template<short int <anonymous>, short int <anonymous>, short int
// <anonymous>, int <anonymous> > class Derived> struct AllocFactor_Core'|
};
template<short Mantissa, short Exponent, short Base, int Tag>
short AllocFactorScientific<Mantissa, Exponent, Base,Tag>::base_ = Base;
Please see comment in code (3 lines above). Basically what happens is that if I have the AllocFactor_Core class as a regular non-template class and when with using directive I just provide name of this class followed by :: and fnc name it works, but as soon as I declare AllocFactor_Core as a template and I try to provide params for it I'm getting beforementioned error.
Your base class expects a template template parameter, but you are passing it a concrete class. Use e.g. the following instead:
using AllocFactor_Core< ::AllocFactorScientific >::getFactor;
Note that the following doesn't work either because of the class name injection:
using AllocFactor_Core< AllocFactorScientific >::getFactor;
AllocFactor_Core<AllocFactorScientific<Mantissa,Exponent,Base,Tag>> should be AllocFactor_Core<AllocFactorScientific>
When typing nested template parameters, be sure to include at least one space between each >
C++ parser gets confused by
AllocFactor_Core<AllocFactorScientific<Mantissa,Exponent,Base,Tag>>
It should be
AllocFactor_Core<AllocFactorScientific<Mantissa,Exponent,Base,Tag> >