I have this problem:
template<typename T> class Bubu
{
...
int (*comparer)(const T t1, const T t2);
...
public:
Bubu(int (*_comparer)(const T t1, const T t2))
{
comparer = _comparer;
}
};
And in another file:
Bubu<char*> asd(strcmp);
Error:
error C2664: 'Bubu<T>::Bubu(int (__cdecl *)(const T,const T))' :
cannot convert parameter 1 from 'int (__cdecl *)(const char *,
const char *)' to 'int (__cdecl *)(const T,const T)'
I don't understand why. Shouldn't the compiler see a "char*" instead of "T" there?
EDIT: the Ideone.com-ready code:
int asdf(const char* a, const char* b)
{ return 0; }
template class Bubu
{
int (*comparer)(const T t1, const T t2);
public:
Bubu(int (*_comparer)(const T t1, const T t2))
{
comparer = _comparer;
}
};
int main(int argc, char* argv[])
{
Bubu asd(asdf);
}
When T is char*, const T is char* const which isn't the same thing as const char *. You need:
Bubu<const char*> asd(strcmp);
Top level const is ignored for function signatures so
int (*)( const char* const, const char* const );
is the same type as
int (*)( const char*, const char* );
so you're OK on the extra top level const although it doesn't gain you anything over the simpler int (*comparer)(T t1, T t2);.
If T is specialized as char*, const T means char* const (i.e. immutable pointer to a mutable char), rather than const char* == char const* (i.e. mutable pointer to an immutable char).
Bubu<const char*> asd(strcmp)
will compile.
Not sure if this is your problem, but your * in your function pointer declaration is in the wrong place (at least compared to what I've ever seen). Instead of:
int (comparer*)(const T t1, const T t2);
It should be:
int (*comparer)(const T t1, const T t2);
I think this does what you want:
#include <cstring>
#include <iostream>
template<class T>
class Bubu {
public:
typedef int (*Comparator)(T, T);
Bubu(Comparator inComparator) : mComparator(inComparator) { }
int compare(T a, T b)
{
return mComparator(a, b);
}
private:
Comparator mComparator;
};
int main()
{
Bubu<const char*> obj(strcmp);
std::cout << obj.compare("one", "two") << std::endl;
return 0;
}
Related
I have some class with enum variable and I wants to stringify that enum. For that reason I added typeValue with stringified values. Also I added separate class for getting stringified values but I can't pass variables in correct way.
Code like this:
#include <iostream>
#include <string>
#include <algorithm>
#define stringify(name) #name
struct MyClass
{
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
MyClass(TYPE t)
:_type(t)
{
}
TYPE type()
{
return _type;
}
inline static const char* typeValue[10] =
{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
/**
* Get enum type string by value
*/
template <class EnumType>
static std::string getEnumTypeName(const EnumType& t, const char (*typeValues)[10])
{
std::string s(typeValues[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
int main()
{
MyClass a(MyClass::TYPE::CLIENT);
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
}
Errors happened:
main.cpp:55:90: error: no matching function for call to ‘EnumStringify::getEnumTypeName(MyClass::TYPE, const char* [10])’
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
^
main.cpp:45:21: note: candidate: template static std::string EnumStringify::getEnumTypeName(const EnumType&, const char (*)[10])
static std::string getEnumTypeName(const EnumType& t, const char (*typeValues)[10])
^~~~~~~~~~~~~~~
main.cpp:45:21: note: template argument deduction/substitution failed:
main.cpp:55:90: note: cannot convert ‘MyClass::typeValue’ (type ‘const char* [10]’) to type ‘const char (*)[10]’
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
Please help me to make it correct. Maybe with typedef would be better?
Two issues:
const char(*name)[] defines a pointer to an array of const chars, not strings.
MyClass::typeValue is of type const char* (&)[10] so it cannot be implicitly converted to a pointer to that array.
This works:
class MyClass{
//...
constexpr static const char* typeValue[10] = {
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
/**
* Get enum type string by value
*/
template <class EnumType>
static std::string getEnumTypeName(const EnumType& t, const char* const (&typeValues )[10])
{
std::string s(typeValues[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
I also added constexpr and pass the value by const reference since you do not want to change it.
My advice would to be use std::array, it alleviates all the issues with passing arrays around.
std::array solution
#include <iostream>
#include <string>
#include <algorithm>
#include <array>
#define stringify(name) #name
struct MyClass
{
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
MyClass(TYPE t)
:_type(t)
{
}
TYPE type()
{
return _type;
}
constexpr static auto typeValue = std::array{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
template <class EnumType, class Array>
static std::string getEnumTypeName(const EnumType& t, const Array& array)
{
std::string s(array[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
int main()
{
MyClass a(MyClass::TYPE::CLIENT);
std::cout << EnumStringify::getEnumTypeName(a.type(), MyClass::typeValue).c_str();
}
Template variable
If I were to write such code, I would use template variable which the user can specialize to provide strings for their own enums:
//
// "Stringify Library"
#define stringify(name) #name
struct MissingEnumTable{};
template<class Enum>
constexpr MissingEnumTable stringified_enum{};
template <class E>
static std::string getEnumTypeName(const E& t)
{
static_assert(!std::is_same_v<decltype(stringified_enum<E>),MissingEnumTable>,
"Enum E is missing stringified_enum table specialization.");
std::string s(stringified_enum<E>[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
// END
// Library user
// - Provide strings for a custom array
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
template<>
constexpr auto stringified_enum<TYPE> = std::array{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
int main()
{
std::cout << getEnumTypeName(TYPE::UNKNOWN).c_str();
}
I wrote a utility function that allows me to write the elements of a std::vector to a std::ostream separated by a delimiter. (I know there's another question about achieving this end, but this question is about why my implementation doesn't work.)
template<typename I, typename S>
struct Joiner {
const I &iterable;
const S &separator;
};
template<typename I, typename S>
inline auto join(const I &iterable, const S &separator) -> const Joiner<I, S> {
return Joiner<I, S>{iterable, separator};
}
template<typename I, typename S>
std::ostream &operator<<(std::ostream &stream, const Joiner<I, S> &joiner) {
auto i = joiner.iterable.begin();
auto end = joiner.iterable.end();
if (i != end) {
stream << *i++;
while (i != end) {
stream << joiner.separator << *i++;
}
}
return stream;
}
Nothing unusual here.
But sometimes what's in the vector isn't the thing I want to output. For example it might be a vector<int *> and I want to output the int values, not the pointers.
So I thought, instead of assuming that the value to print is *i, let's pass in an "extractor" so that, in the case of the vector<int *> I could specify **i.
I've tried to do this a couple of ways and it's not really coming together. My current thinking is that template parameter X is the eXtracted type, and the join method should take a std::function that returns that type. The type of the extractor function's parameter would be I::iterator.
template<typename I, typename S, typename X>
struct Joiner {
const I &iterable;
const S &separator;
const std::function<X(typename I::iterator)> extract;
};
template<typename I, typename S, typename X>
inline auto join(const I &iterable, const S &separator, std::function<X(typename I::iterator)> extract) -> const Joiner<I, S, X> {
return Joiner<I, S, X>{iterable, separator, extract};
}
And then, in the operator << implementation, I use joiner.extract(i++) instead of *i++.
However, this doesn't work. I wrote this test program:
int main(int argc, char *argv[]) {
int a = 5;
int b = 20;
int c = 25;
std::vector<int *> v{&a, &b, &c};
std::cout << join<std::vector<int *>, std::string, int>(v, ",", [](std::vector<int *>::iterator i) { return **i; }) << std::endl;
}
When compiling it I get errors like this:
/Users/wboyce/git/mathias/util.hpp:31:19: error: no matching function for call to object of type 'const std::function<int (typename vector<int *, allocator<int *> >::iterator)>' (aka 'const function<int (__wrap_iter<int **>)>')
stream << joiner.extract(i++);
^~~~~~~~~~~~~~
/Users/wboyce/git/mathias/scratch.cpp:19:15: note: in instantiation of function template specialization 'operator<<<std::__1::vector<int *, std::__1::allocator<int *> >, std::__1::basic_string<char>, int>' requested here
std::cout << join<std::vector<int *>, std::string, int>(v, ",", [](const std::vector<int *>::iterator i) { return **i; }) << std::endl;
^
/usr/local/opt/llvm/bin/../include/c++/v1/functional:2255:9: note: candidate function not viable: no known conversion from '__wrap_iter<int *const *>' to '__wrap_iter<int **>' for 1st argument
_Rp operator()(_ArgTypes...) const;
I think the source of my trouble is that the iterable type is I but I'm using it at const I & which means that the iterator is going to be returning something like const I::iterator but here's where things get fuzzy for me.
I don't think that the iterator type is really const I::iterator because that is a const iterator over I not a non-const iterator over const I.
Does that mean the iterator is going to be returning const X or should the constness be baked into X?
Can the compiler be made to infer the type arguments to join so I don't have to specify them?
What else do I need to do to make this work?
And is this even the right approach? I initially thought that X would be the type of the extractor function itself... but I couldn't make that work either.
The primary reason (or one of the primary reasons) to use std::function is when type erasure is required.
There is no need for type erasure here.
#include <iostream>
#include <functional>
#include <vector>
template<typename I, typename S, typename E>
struct Joiner {
const I &iterable;
const S &separator;
E e;
};
template<typename I, typename S, typename E>
inline auto join(const I &iterable, const S &separator,
E &&e) -> const Joiner<I, S, E> {
return Joiner<I, S, E>{iterable, separator, std::forward<E>(e)};
}
template<typename I, typename S, typename E>
std::ostream &operator<<(std::ostream &stream, const Joiner<I, S, E> &joiner) {
auto i = joiner.iterable.begin();
auto end = joiner.iterable.end();
if (i != end) {
stream << joiner.e(*i++);
while (i != end) {
stream << joiner.separator << joiner.e(*i++);
}
}
return stream;
}
int main(int argc, char *argv[]) {
int a = 5;
int b = 20;
int c = 25;
std::vector<int *> v{&a, &b, &c};
std::cout << join(v, ",", [](int *p) { return *p; }) << std::endl;
}
I am trying to write a template specialization for a function that returns the maximum value of an array of numeric values (general version) or the longest c-string of an array of c-strings (specialization). If I do not use const-ness, my function prototypes look like this
template <typename T>
T maxn(T* my_Tptr, unsigned int n);
template <>
char* maxn <char*> (char** my_cstrings, unsigned int n);
and my code compiles.
However, if I try to use const-ness, my function prototypes look like this,
template <typename T>
T maxn(const T* my_Tptr, unsigned int n);
template <>
char* maxn <char*> (const char** my_cstrings, unsigned int n);
my code does not compile, and the compiler (gcc) prints this error:
error: template-id 'maxn' for 'char* maxn(const char**, unsigned int)' does not match any template declaration.
Where am I going wrong?
The problem is in the constness. If you look closely const T* my_Tptr means my_Tptr is a pointer to const T. But const char** my_Tptr means Tptr is a pointer to pointer to const char. So the type moves from pointer to const T to pointer to pointer to const T. If you make it char* const* my_Tptr* then it will work, since then the type will be pointer to const char pointer. The specialization is pointer to const T*->pointer to const char*
Not sure what is the whole logic behind it but if you change your template definition to say that you are expecting pointers that will help:
template <typename T>
T* maxn(const T** my_Tptr, unsigned int n);
template <>
char* maxn(const char** my_cstrings, unsigned int n);
You might provide several overloads for the char-case to resolve the issue:
#include <iostream>
#include <stdexcept>
template <typename T>
T maxn(const T* const data, unsigned int n) {
throw std::logic_error("Failure");
}
const char* maxn(const char * const * data, unsigned int n) {
return "Success";
}
inline const char* maxn(const char** data, unsigned int n) {
return maxn(static_cast<const char * const *>(data), n);
}
inline const char* maxn(char* const * data, unsigned int n) {
return maxn(static_cast<const char * const *>(data), n);
}
inline const char* maxn(char** data, unsigned int n) {
return maxn(static_cast<const char * const *>(data), n);
}
int main() {
const char* a[] = { "A", "B", "C" };
std::cout << maxn((const char * const *)a, 3) << '\n';
std::cout << maxn((const char **)a, 3) << '\n';
std::cout << maxn((char * const *)a, 3) << '\n';
std::cout << maxn((char**)a, 3) << '\n';
}
This compiles fine:
template <>
char* maxn(char* const* my_cstrings, unsigned int n);
It accepts a pointer to a const char pointer like specified in the base template.
I want to be able to differentiate array from pointers in overload resolution :
class string {
public:
string(const char* c_str);
template<int N>
string(const char (&str) [N]);
};
int main() {
const char* c_str = "foo";
string foo(c_str); // ok will call string(const char*)
string bar("bar"); // call string(const char*) instead of the array version
}
The best I have found so far is to use a reference to the pointer instead of a pointer :
class string {
public:
string(const char*& c_str);
template<int N>
string(const char (&str) [N]);
};
int main() {
const char* c_str = "foo";
string foo(c_str); // ok will call string(const char*)
string bar("bar"); // ok, will call the array version
}
it's not exactly the same thing and I want to know if a better way exist
You need to make the first overload a poorer choice when both are viable. Currently they are a tie on conversion ranking (both are "Exact Match"), and then the tie is broken because non-templates are preferred.
This ought to make the conversion ranking worse:
struct stg
{
struct cvt { const char* p; cvt(const char* p_p) : p(p_p) {} };
// matches const char*, but disfavored in overload ranking
stg(cvt c_str); // use c_str.p inside :( Or add an implicit conversion
template<int N>
stg(const char (&str) [N]);
};
You can use SFINAE. This might not be the best way, but it should work ok:
//thanks to dyp for further reduction
template<typename T, typename = typename std::enable_if<std::is_same<T, char>::value>::type>
string(const T * const &) {std::cout << "const char *\n";}
template<std::size_t N> //credit to jrok for noticing the unnecessary SFINAE
string(const char(&)[N]) {std::cout << "const char(&)[" << N << "]\n";}
Here's a live example.
A more generic version of this problem could be detected as follows.
template <class T>
void func(T,
typename std::enable_if<std::is_pointer<T>::value, void>::type * = 0)
{
// catch ptr
}
template <class T, int N>
void func(T (&)[N])
{
//catch array
}
int main(void)
{
int arr[5];
char *b = 0;
func(arr); // catch array
func(b); // catch ptr
}
I want to be able to specialize based on a constant c style string. The problem is that when I call my templatized function the type is const char[N] where 'N' is the size of the string +1 (null character). How can I specialize for all c-style strings?
The following code displays the problem. You can see the specialization for const char [15] gets matched for "const char [15]" but for "const char[5]" it goes to Generic.
Is there any way to do this?
template <typename T>
struct Test {
static const char* type() { return "Generic"; }
};
template <>
struct Test<const char*> {
static const char* type() { return "const char*"; }
};
template <>
struct Test<const char[]> {
static const char* type() { return "const char[]"; }
};
template <>
struct Test<const char[15]> {
static const char* type() { return "const char[15]"; }
};
template <>
struct Test<char*> {
static const char* type() { return "char*"; }
};
template <>
struct Test<char[]> {
static const char* type() { return "char[]"; }
};
template <typename T>
void PrintType(const T& expected) {
std::cerr << expected << " type " << Test<T>::type() << std::endl;
}
int main(int argc, char* argv[]) {
const char* tmp = "const char*";
PrintType(tmp);
PrintType("const char[]");
PrintType("const char[15]");
PrintType("const char[5]");
}
output when run in Windows 7 - VS 2008
const char* type const char*
const char[] type Generic
const char[15] type const char[15]
const char[5] type Generic
A specialization for any array of chars is:
template< std::size_t N >
struct Test< const char[N] > { ... };
However you can no longer return a char* from type(), unless you write more metafunctions to turn a non-type template parameter into its textual representation.
Try this:
#include <cstdio>
#include <string>
template<class T>
void f2(const T& s) // Handle all kinds of string objects
{ std::printf("string object: %s\n", s.c_str()); }
void f2(const char* s) // Handle const char*
{ std::printf("const char*: %s\n", s); }
// ----------------------------------------------------------------------------
template<size_t N>
void f(const char(&s)[N]) // Handle const char array
{ std::printf("const char[%zu]: %s\n", N, s); }
template<size_t N>
void f(char(&s)[N]) // Handle char array
{ std::printf("char[%zu]: %s\n", N, s); }
template<class T>
inline void f(T&& s) // Handle other cases
{ f2(std::forward<T>(s)); }
int main() {
std::string stringObj = "some kind of string object ...";
char charArr[] = "char array";
const char constCharArr[] = "const char array";
const char* constCharPtr = "const char pointer";
f(stringObj);
f(charArr);
f(constCharArr);
f(constCharPtr);
//f("const char array");
}
Output:
string object: some kind of string object ...
char[11]: char array
const char[17]: const char array
const char*: const char pointer
Explanation
f() has two kinds of overloads: One for char arrays and one for "everything else".
f2() handles the "everything else" case.