Try to make meta function which find method “size” in class
I am trying to write function getSize() which takes some template argument, tries to find method or field in this argument and return size() or size.
my code is:
#include <iostream>
#include <vector>
#include <utility>
#include <string>
#include <type_traits>
template <typename T>
class has_size {
private:
typedef char Yes;
typedef Yes No[2];
template <typename U, U> struct really_has;
template<typename C> static Yes& Test(really_has <size_t (C::*)() const, &C::size>*);
template<typename C> static Yes& Test(really_has <size_t (C::*)(), &C::size>*);
template<typename> static No& Test(...);
public:
static bool const value = sizeof(Test<T>(0)) == sizeof(Yes);
};
template <class T>
size_t get_size(T t){
size_t res = 0;
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main() {
std::vector<float> v(10);
std::cout << std::boolalpha << has_size<std::vector<float>>::value << std::endl;
std::cout << std::boolalpha << has_size<std::string>::value << std::endl;
size_t res = get_size(v);
std::cout<< res;
return 0;
}
The function has_size performs rightly in my example, but when I try to call getSize I got error:
prog.cpp: In function ‘int main()’:
prog.cpp:47:24: error: the value of ‘v’ is not usable in a constant expression
size_t res = get_size<v>;
^
prog.cpp:43:21: note: ‘v’ was not declared ‘constexpr’
std::vector<float> v(10);
^
prog.cpp:47:15: error: cannot resolve overloaded function ‘get_size’ based on conversion to type ‘size_t {aka long unsigned int}’
size_t res = get_size<v>;
^~~~~~~~~~~
c++ c++11 template-meta-programming
add a comment |
I am trying to write function getSize() which takes some template argument, tries to find method or field in this argument and return size() or size.
my code is:
#include <iostream>
#include <vector>
#include <utility>
#include <string>
#include <type_traits>
template <typename T>
class has_size {
private:
typedef char Yes;
typedef Yes No[2];
template <typename U, U> struct really_has;
template<typename C> static Yes& Test(really_has <size_t (C::*)() const, &C::size>*);
template<typename C> static Yes& Test(really_has <size_t (C::*)(), &C::size>*);
template<typename> static No& Test(...);
public:
static bool const value = sizeof(Test<T>(0)) == sizeof(Yes);
};
template <class T>
size_t get_size(T t){
size_t res = 0;
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main() {
std::vector<float> v(10);
std::cout << std::boolalpha << has_size<std::vector<float>>::value << std::endl;
std::cout << std::boolalpha << has_size<std::string>::value << std::endl;
size_t res = get_size(v);
std::cout<< res;
return 0;
}
The function has_size performs rightly in my example, but when I try to call getSize I got error:
prog.cpp: In function ‘int main()’:
prog.cpp:47:24: error: the value of ‘v’ is not usable in a constant expression
size_t res = get_size<v>;
^
prog.cpp:43:21: note: ‘v’ was not declared ‘constexpr’
std::vector<float> v(10);
^
prog.cpp:47:15: error: cannot resolve overloaded function ‘get_size’ based on conversion to type ‘size_t {aka long unsigned int}’
size_t res = get_size<v>;
^~~~~~~~~~~
c++ c++11 template-meta-programming
Here you have example has_onEnter
– Dawid Drozd
Nov 16 '18 at 19:07
1
In your code probably you want to write get_size(v)
– Dawid Drozd
Nov 16 '18 at 19:09
Thanks! It is my bug...
– Semyon
Nov 18 '18 at 12:13
add a comment |
I am trying to write function getSize() which takes some template argument, tries to find method or field in this argument and return size() or size.
my code is:
#include <iostream>
#include <vector>
#include <utility>
#include <string>
#include <type_traits>
template <typename T>
class has_size {
private:
typedef char Yes;
typedef Yes No[2];
template <typename U, U> struct really_has;
template<typename C> static Yes& Test(really_has <size_t (C::*)() const, &C::size>*);
template<typename C> static Yes& Test(really_has <size_t (C::*)(), &C::size>*);
template<typename> static No& Test(...);
public:
static bool const value = sizeof(Test<T>(0)) == sizeof(Yes);
};
template <class T>
size_t get_size(T t){
size_t res = 0;
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main() {
std::vector<float> v(10);
std::cout << std::boolalpha << has_size<std::vector<float>>::value << std::endl;
std::cout << std::boolalpha << has_size<std::string>::value << std::endl;
size_t res = get_size(v);
std::cout<< res;
return 0;
}
The function has_size performs rightly in my example, but when I try to call getSize I got error:
prog.cpp: In function ‘int main()’:
prog.cpp:47:24: error: the value of ‘v’ is not usable in a constant expression
size_t res = get_size<v>;
^
prog.cpp:43:21: note: ‘v’ was not declared ‘constexpr’
std::vector<float> v(10);
^
prog.cpp:47:15: error: cannot resolve overloaded function ‘get_size’ based on conversion to type ‘size_t {aka long unsigned int}’
size_t res = get_size<v>;
^~~~~~~~~~~
c++ c++11 template-meta-programming
I am trying to write function getSize() which takes some template argument, tries to find method or field in this argument and return size() or size.
my code is:
#include <iostream>
#include <vector>
#include <utility>
#include <string>
#include <type_traits>
template <typename T>
class has_size {
private:
typedef char Yes;
typedef Yes No[2];
template <typename U, U> struct really_has;
template<typename C> static Yes& Test(really_has <size_t (C::*)() const, &C::size>*);
template<typename C> static Yes& Test(really_has <size_t (C::*)(), &C::size>*);
template<typename> static No& Test(...);
public:
static bool const value = sizeof(Test<T>(0)) == sizeof(Yes);
};
template <class T>
size_t get_size(T t){
size_t res = 0;
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main() {
std::vector<float> v(10);
std::cout << std::boolalpha << has_size<std::vector<float>>::value << std::endl;
std::cout << std::boolalpha << has_size<std::string>::value << std::endl;
size_t res = get_size(v);
std::cout<< res;
return 0;
}
The function has_size performs rightly in my example, but when I try to call getSize I got error:
prog.cpp: In function ‘int main()’:
prog.cpp:47:24: error: the value of ‘v’ is not usable in a constant expression
size_t res = get_size<v>;
^
prog.cpp:43:21: note: ‘v’ was not declared ‘constexpr’
std::vector<float> v(10);
^
prog.cpp:47:15: error: cannot resolve overloaded function ‘get_size’ based on conversion to type ‘size_t {aka long unsigned int}’
size_t res = get_size<v>;
^~~~~~~~~~~
c++ c++11 template-meta-programming
c++ c++11 template-meta-programming
edited Nov 18 '18 at 12:14
asked Nov 16 '18 at 19:00
Semyon
184
184
Here you have example has_onEnter
– Dawid Drozd
Nov 16 '18 at 19:07
1
In your code probably you want to write get_size(v)
– Dawid Drozd
Nov 16 '18 at 19:09
Thanks! It is my bug...
– Semyon
Nov 18 '18 at 12:13
add a comment |
Here you have example has_onEnter
– Dawid Drozd
Nov 16 '18 at 19:07
1
In your code probably you want to write get_size(v)
– Dawid Drozd
Nov 16 '18 at 19:09
Thanks! It is my bug...
– Semyon
Nov 18 '18 at 12:13
Here you have example has_onEnter
– Dawid Drozd
Nov 16 '18 at 19:07
Here you have example has_onEnter
– Dawid Drozd
Nov 16 '18 at 19:07
1
1
In your code probably you want to write get_size(v)
– Dawid Drozd
Nov 16 '18 at 19:09
In your code probably you want to write get_size(v)
– Dawid Drozd
Nov 16 '18 at 19:09
Thanks! It is my bug...
– Semyon
Nov 18 '18 at 12:13
Thanks! It is my bug...
– Semyon
Nov 18 '18 at 12:13
add a comment |
3 Answers
3
active
oldest
votes
So upgrading your code little bit: (for c++11)
struct MyStruct{
int size = 12;
};
// This function will compile only if has_size is true
template <class T,
typename std::enable_if<has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size();
}
// This function will compile only if has_size is FALSE (check negation !has_size)
template <class T,
typename std::enable_if<!has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size;
}
int main(){
std::vector<float> v(10);
std::cout << get_size(v) << std::endl;
MyStruct my;
std::cout << get_size(my) << std::endl;
return 0;
}
Documentation about std::enable_if
So I used case #4, enabled via a template parameter.
So each case of function of get_size
will exist in final program depending on enable_if
result. So compiler will ignore not meeting our conditions function to compile.
So upgrading your code little bit: (from c++17)
template <class T>
size_t get_size(const T& t){
size_t res = 0;
if constexpr(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main(){
std::vector<float> v(10);
std::cout<< get_size(v) << std::endl;
return 0;
}
So less code and more readable :)
This solution is using feature from C++17 if constexpr
Why your solution isn't working:
if(has_size<T>::value){ // <--- this is compile time result (has_size<T>::value) so always true or always false depends on template argument which is deduced from argument type
res = t.size(); // this need to compile always, so if it is vector then ok if something else that doesn't have such method will fail to compile
}else{
res = t.size; // this need to compile always, again as above
}
From smaller bugs/improvements:
- pass by
const&
;)
size_t res = get_size<v>;
should beget_size(v)
template argument will be deduced. But yeah you can write alsoget_size<std::vector>(v)
add a comment |
There's a lot going on here that needs to be fixed. To start, in your main size_t res = get_size<v>;
isn't going to work, because you can't have v
be a template argument, I'm assuming this was meant to be get_size(v)
instead.
In get_size
you have this
if (has_size<T>::value) {
res = t.size();
} else {
res = t.size;
}
This isn't going to work because even though only one is used, the compiler sees you doing both t.size
and t.size()
. I see your question is tagged c++11 so I will provide a c++11 answer.
First, I'm going to create some very simple classes to use, one with a member function and one with a data member
// using distinc values 7 and 3 to differentiate easily later
struct SizeData {
std::size_t size = 7;
};
struct SizeFunc {
std::size_t size() const { return 3; };
};
I'm going to also write a basic void_t
for metaprogramming, and use a very standard modern metaprogramming approach to check if a given type has a .size()
member function. (it looks like the technique you are attempting is outdated).
template <typename>
using void_type = void;
template <typename T, typename = void>
struct HasSizeFunc : std::false_type { };
template <typename T>
struct HasSizeFunc<T, void_type<decltype(std::declval<const T&>().size())>>
: std::true_type { };
I can use this very easily in a main to check whether something has a .size()
or not
int main() {
std::cout << "SizeFunc: " << HasSizeFunc<SizeFunc>::value << 'n';
std::cout << "SizeData: " << HasSizeFunc<SizeData>::value << 'n';
}
But now for the get_size()
function. As I said earlier, your if/else won't work because both branches aren't compilable (if constexpr
works but isn't available in c++11). So instead you can do what's called "tag dispatch" to decide which overload of a function to call in order to call the right .size
// std::size_t may not be right for every type. leaving it for simplicity.
template <typename T>
std::size_t get_size_impl(T t, std::true_type) {
return t.size();
}
template <typename T>
std::size_t get_size_impl(T t, std::false_type) {
return t.size;
}
template <typename T>
std::size_t get_size(T t) { // note, this should probably be a const reference
// second argument used to select an overload of get_size_impl
return get_size_impl(t, HasSizeFunc<T>{});
}
And to use it:
int main() {
SizeFunc sf;
std::cout << "SizeFunc: " << get_size(sf) << 'n';
SizeData sd;
std::cout << "SizeData: " << get_size(sd) << 'n';
}
click here to see all the code in one live example. I recommend watching these cppcon talks to learn more.
Also, here is what I'd do in c++17
1
I like your solution but (my question) what if we have more cases ? I mean here you only assume.size()
and.size
how to handle 3+ case ? eg..length
? We should use what kind of type ? Or maybe use extra template argument likeget_size_impl<0>(t), get_size_impl<1>(t)...
?
– Dawid Drozd
Nov 20 '18 at 11:10
1
@DawidDrozd tag dispatch still works, but you need more than two tags here is an expanded example of the c++11 version. This approach is used by the standard library with different iterators for example, inside something likestd::distance
– Ryan Haining
Nov 20 '18 at 17:05
1
@DawidDrozd also to switch it up for c++17 you can do a similar thing withintegral_constant
andif constexpr
, live example here
– Ryan Haining
Nov 21 '18 at 17:51
add a comment |
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
these branches are evaluated at runtime. So both branches must be valid at compile time.
#define RETURNS(...)
noexcept(noexcept(__VA_ARGS__))
-> decltype(__VA_ARGS__)
{ return __VA_ARGS__; }
template<class S, class...Ts>
auto select( S, Ts&&...ts )
RETURNS( std::get<S::value>(std::forward_as_tuple( std::forward<Ts>(ts)... )) )
which gives you a compile-time branch.
struct call_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size() )
};
struct get_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size )
};
auto f = select(has_size<T>{},
get_size_t{},
call_size_t{}
};
res = f(t);
this is quite annoying because you are in c++11; code is less than half this in c++14 and becomes trivial in c++17.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53343884%2ftry-to-make-meta-function-which-find-method-size-in-class%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
So upgrading your code little bit: (for c++11)
struct MyStruct{
int size = 12;
};
// This function will compile only if has_size is true
template <class T,
typename std::enable_if<has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size();
}
// This function will compile only if has_size is FALSE (check negation !has_size)
template <class T,
typename std::enable_if<!has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size;
}
int main(){
std::vector<float> v(10);
std::cout << get_size(v) << std::endl;
MyStruct my;
std::cout << get_size(my) << std::endl;
return 0;
}
Documentation about std::enable_if
So I used case #4, enabled via a template parameter.
So each case of function of get_size
will exist in final program depending on enable_if
result. So compiler will ignore not meeting our conditions function to compile.
So upgrading your code little bit: (from c++17)
template <class T>
size_t get_size(const T& t){
size_t res = 0;
if constexpr(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main(){
std::vector<float> v(10);
std::cout<< get_size(v) << std::endl;
return 0;
}
So less code and more readable :)
This solution is using feature from C++17 if constexpr
Why your solution isn't working:
if(has_size<T>::value){ // <--- this is compile time result (has_size<T>::value) so always true or always false depends on template argument which is deduced from argument type
res = t.size(); // this need to compile always, so if it is vector then ok if something else that doesn't have such method will fail to compile
}else{
res = t.size; // this need to compile always, again as above
}
From smaller bugs/improvements:
- pass by
const&
;)
size_t res = get_size<v>;
should beget_size(v)
template argument will be deduced. But yeah you can write alsoget_size<std::vector>(v)
add a comment |
So upgrading your code little bit: (for c++11)
struct MyStruct{
int size = 12;
};
// This function will compile only if has_size is true
template <class T,
typename std::enable_if<has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size();
}
// This function will compile only if has_size is FALSE (check negation !has_size)
template <class T,
typename std::enable_if<!has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size;
}
int main(){
std::vector<float> v(10);
std::cout << get_size(v) << std::endl;
MyStruct my;
std::cout << get_size(my) << std::endl;
return 0;
}
Documentation about std::enable_if
So I used case #4, enabled via a template parameter.
So each case of function of get_size
will exist in final program depending on enable_if
result. So compiler will ignore not meeting our conditions function to compile.
So upgrading your code little bit: (from c++17)
template <class T>
size_t get_size(const T& t){
size_t res = 0;
if constexpr(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main(){
std::vector<float> v(10);
std::cout<< get_size(v) << std::endl;
return 0;
}
So less code and more readable :)
This solution is using feature from C++17 if constexpr
Why your solution isn't working:
if(has_size<T>::value){ // <--- this is compile time result (has_size<T>::value) so always true or always false depends on template argument which is deduced from argument type
res = t.size(); // this need to compile always, so if it is vector then ok if something else that doesn't have such method will fail to compile
}else{
res = t.size; // this need to compile always, again as above
}
From smaller bugs/improvements:
- pass by
const&
;)
size_t res = get_size<v>;
should beget_size(v)
template argument will be deduced. But yeah you can write alsoget_size<std::vector>(v)
add a comment |
So upgrading your code little bit: (for c++11)
struct MyStruct{
int size = 12;
};
// This function will compile only if has_size is true
template <class T,
typename std::enable_if<has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size();
}
// This function will compile only if has_size is FALSE (check negation !has_size)
template <class T,
typename std::enable_if<!has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size;
}
int main(){
std::vector<float> v(10);
std::cout << get_size(v) << std::endl;
MyStruct my;
std::cout << get_size(my) << std::endl;
return 0;
}
Documentation about std::enable_if
So I used case #4, enabled via a template parameter.
So each case of function of get_size
will exist in final program depending on enable_if
result. So compiler will ignore not meeting our conditions function to compile.
So upgrading your code little bit: (from c++17)
template <class T>
size_t get_size(const T& t){
size_t res = 0;
if constexpr(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main(){
std::vector<float> v(10);
std::cout<< get_size(v) << std::endl;
return 0;
}
So less code and more readable :)
This solution is using feature from C++17 if constexpr
Why your solution isn't working:
if(has_size<T>::value){ // <--- this is compile time result (has_size<T>::value) so always true or always false depends on template argument which is deduced from argument type
res = t.size(); // this need to compile always, so if it is vector then ok if something else that doesn't have such method will fail to compile
}else{
res = t.size; // this need to compile always, again as above
}
From smaller bugs/improvements:
- pass by
const&
;)
size_t res = get_size<v>;
should beget_size(v)
template argument will be deduced. But yeah you can write alsoget_size<std::vector>(v)
So upgrading your code little bit: (for c++11)
struct MyStruct{
int size = 12;
};
// This function will compile only if has_size is true
template <class T,
typename std::enable_if<has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size();
}
// This function will compile only if has_size is FALSE (check negation !has_size)
template <class T,
typename std::enable_if<!has_size<T>::value, int>::type = 0>
size_t get_size(const T& t){
return t.size;
}
int main(){
std::vector<float> v(10);
std::cout << get_size(v) << std::endl;
MyStruct my;
std::cout << get_size(my) << std::endl;
return 0;
}
Documentation about std::enable_if
So I used case #4, enabled via a template parameter.
So each case of function of get_size
will exist in final program depending on enable_if
result. So compiler will ignore not meeting our conditions function to compile.
So upgrading your code little bit: (from c++17)
template <class T>
size_t get_size(const T& t){
size_t res = 0;
if constexpr(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
return res;
}
int main(){
std::vector<float> v(10);
std::cout<< get_size(v) << std::endl;
return 0;
}
So less code and more readable :)
This solution is using feature from C++17 if constexpr
Why your solution isn't working:
if(has_size<T>::value){ // <--- this is compile time result (has_size<T>::value) so always true or always false depends on template argument which is deduced from argument type
res = t.size(); // this need to compile always, so if it is vector then ok if something else that doesn't have such method will fail to compile
}else{
res = t.size; // this need to compile always, again as above
}
From smaller bugs/improvements:
- pass by
const&
;)
size_t res = get_size<v>;
should beget_size(v)
template argument will be deduced. But yeah you can write alsoget_size<std::vector>(v)
edited Nov 16 '18 at 19:46
answered Nov 16 '18 at 19:19
Dawid Drozd
7,93544152
7,93544152
add a comment |
add a comment |
There's a lot going on here that needs to be fixed. To start, in your main size_t res = get_size<v>;
isn't going to work, because you can't have v
be a template argument, I'm assuming this was meant to be get_size(v)
instead.
In get_size
you have this
if (has_size<T>::value) {
res = t.size();
} else {
res = t.size;
}
This isn't going to work because even though only one is used, the compiler sees you doing both t.size
and t.size()
. I see your question is tagged c++11 so I will provide a c++11 answer.
First, I'm going to create some very simple classes to use, one with a member function and one with a data member
// using distinc values 7 and 3 to differentiate easily later
struct SizeData {
std::size_t size = 7;
};
struct SizeFunc {
std::size_t size() const { return 3; };
};
I'm going to also write a basic void_t
for metaprogramming, and use a very standard modern metaprogramming approach to check if a given type has a .size()
member function. (it looks like the technique you are attempting is outdated).
template <typename>
using void_type = void;
template <typename T, typename = void>
struct HasSizeFunc : std::false_type { };
template <typename T>
struct HasSizeFunc<T, void_type<decltype(std::declval<const T&>().size())>>
: std::true_type { };
I can use this very easily in a main to check whether something has a .size()
or not
int main() {
std::cout << "SizeFunc: " << HasSizeFunc<SizeFunc>::value << 'n';
std::cout << "SizeData: " << HasSizeFunc<SizeData>::value << 'n';
}
But now for the get_size()
function. As I said earlier, your if/else won't work because both branches aren't compilable (if constexpr
works but isn't available in c++11). So instead you can do what's called "tag dispatch" to decide which overload of a function to call in order to call the right .size
// std::size_t may not be right for every type. leaving it for simplicity.
template <typename T>
std::size_t get_size_impl(T t, std::true_type) {
return t.size();
}
template <typename T>
std::size_t get_size_impl(T t, std::false_type) {
return t.size;
}
template <typename T>
std::size_t get_size(T t) { // note, this should probably be a const reference
// second argument used to select an overload of get_size_impl
return get_size_impl(t, HasSizeFunc<T>{});
}
And to use it:
int main() {
SizeFunc sf;
std::cout << "SizeFunc: " << get_size(sf) << 'n';
SizeData sd;
std::cout << "SizeData: " << get_size(sd) << 'n';
}
click here to see all the code in one live example. I recommend watching these cppcon talks to learn more.
Also, here is what I'd do in c++17
1
I like your solution but (my question) what if we have more cases ? I mean here you only assume.size()
and.size
how to handle 3+ case ? eg..length
? We should use what kind of type ? Or maybe use extra template argument likeget_size_impl<0>(t), get_size_impl<1>(t)...
?
– Dawid Drozd
Nov 20 '18 at 11:10
1
@DawidDrozd tag dispatch still works, but you need more than two tags here is an expanded example of the c++11 version. This approach is used by the standard library with different iterators for example, inside something likestd::distance
– Ryan Haining
Nov 20 '18 at 17:05
1
@DawidDrozd also to switch it up for c++17 you can do a similar thing withintegral_constant
andif constexpr
, live example here
– Ryan Haining
Nov 21 '18 at 17:51
add a comment |
There's a lot going on here that needs to be fixed. To start, in your main size_t res = get_size<v>;
isn't going to work, because you can't have v
be a template argument, I'm assuming this was meant to be get_size(v)
instead.
In get_size
you have this
if (has_size<T>::value) {
res = t.size();
} else {
res = t.size;
}
This isn't going to work because even though only one is used, the compiler sees you doing both t.size
and t.size()
. I see your question is tagged c++11 so I will provide a c++11 answer.
First, I'm going to create some very simple classes to use, one with a member function and one with a data member
// using distinc values 7 and 3 to differentiate easily later
struct SizeData {
std::size_t size = 7;
};
struct SizeFunc {
std::size_t size() const { return 3; };
};
I'm going to also write a basic void_t
for metaprogramming, and use a very standard modern metaprogramming approach to check if a given type has a .size()
member function. (it looks like the technique you are attempting is outdated).
template <typename>
using void_type = void;
template <typename T, typename = void>
struct HasSizeFunc : std::false_type { };
template <typename T>
struct HasSizeFunc<T, void_type<decltype(std::declval<const T&>().size())>>
: std::true_type { };
I can use this very easily in a main to check whether something has a .size()
or not
int main() {
std::cout << "SizeFunc: " << HasSizeFunc<SizeFunc>::value << 'n';
std::cout << "SizeData: " << HasSizeFunc<SizeData>::value << 'n';
}
But now for the get_size()
function. As I said earlier, your if/else won't work because both branches aren't compilable (if constexpr
works but isn't available in c++11). So instead you can do what's called "tag dispatch" to decide which overload of a function to call in order to call the right .size
// std::size_t may not be right for every type. leaving it for simplicity.
template <typename T>
std::size_t get_size_impl(T t, std::true_type) {
return t.size();
}
template <typename T>
std::size_t get_size_impl(T t, std::false_type) {
return t.size;
}
template <typename T>
std::size_t get_size(T t) { // note, this should probably be a const reference
// second argument used to select an overload of get_size_impl
return get_size_impl(t, HasSizeFunc<T>{});
}
And to use it:
int main() {
SizeFunc sf;
std::cout << "SizeFunc: " << get_size(sf) << 'n';
SizeData sd;
std::cout << "SizeData: " << get_size(sd) << 'n';
}
click here to see all the code in one live example. I recommend watching these cppcon talks to learn more.
Also, here is what I'd do in c++17
1
I like your solution but (my question) what if we have more cases ? I mean here you only assume.size()
and.size
how to handle 3+ case ? eg..length
? We should use what kind of type ? Or maybe use extra template argument likeget_size_impl<0>(t), get_size_impl<1>(t)...
?
– Dawid Drozd
Nov 20 '18 at 11:10
1
@DawidDrozd tag dispatch still works, but you need more than two tags here is an expanded example of the c++11 version. This approach is used by the standard library with different iterators for example, inside something likestd::distance
– Ryan Haining
Nov 20 '18 at 17:05
1
@DawidDrozd also to switch it up for c++17 you can do a similar thing withintegral_constant
andif constexpr
, live example here
– Ryan Haining
Nov 21 '18 at 17:51
add a comment |
There's a lot going on here that needs to be fixed. To start, in your main size_t res = get_size<v>;
isn't going to work, because you can't have v
be a template argument, I'm assuming this was meant to be get_size(v)
instead.
In get_size
you have this
if (has_size<T>::value) {
res = t.size();
} else {
res = t.size;
}
This isn't going to work because even though only one is used, the compiler sees you doing both t.size
and t.size()
. I see your question is tagged c++11 so I will provide a c++11 answer.
First, I'm going to create some very simple classes to use, one with a member function and one with a data member
// using distinc values 7 and 3 to differentiate easily later
struct SizeData {
std::size_t size = 7;
};
struct SizeFunc {
std::size_t size() const { return 3; };
};
I'm going to also write a basic void_t
for metaprogramming, and use a very standard modern metaprogramming approach to check if a given type has a .size()
member function. (it looks like the technique you are attempting is outdated).
template <typename>
using void_type = void;
template <typename T, typename = void>
struct HasSizeFunc : std::false_type { };
template <typename T>
struct HasSizeFunc<T, void_type<decltype(std::declval<const T&>().size())>>
: std::true_type { };
I can use this very easily in a main to check whether something has a .size()
or not
int main() {
std::cout << "SizeFunc: " << HasSizeFunc<SizeFunc>::value << 'n';
std::cout << "SizeData: " << HasSizeFunc<SizeData>::value << 'n';
}
But now for the get_size()
function. As I said earlier, your if/else won't work because both branches aren't compilable (if constexpr
works but isn't available in c++11). So instead you can do what's called "tag dispatch" to decide which overload of a function to call in order to call the right .size
// std::size_t may not be right for every type. leaving it for simplicity.
template <typename T>
std::size_t get_size_impl(T t, std::true_type) {
return t.size();
}
template <typename T>
std::size_t get_size_impl(T t, std::false_type) {
return t.size;
}
template <typename T>
std::size_t get_size(T t) { // note, this should probably be a const reference
// second argument used to select an overload of get_size_impl
return get_size_impl(t, HasSizeFunc<T>{});
}
And to use it:
int main() {
SizeFunc sf;
std::cout << "SizeFunc: " << get_size(sf) << 'n';
SizeData sd;
std::cout << "SizeData: " << get_size(sd) << 'n';
}
click here to see all the code in one live example. I recommend watching these cppcon talks to learn more.
Also, here is what I'd do in c++17
There's a lot going on here that needs to be fixed. To start, in your main size_t res = get_size<v>;
isn't going to work, because you can't have v
be a template argument, I'm assuming this was meant to be get_size(v)
instead.
In get_size
you have this
if (has_size<T>::value) {
res = t.size();
} else {
res = t.size;
}
This isn't going to work because even though only one is used, the compiler sees you doing both t.size
and t.size()
. I see your question is tagged c++11 so I will provide a c++11 answer.
First, I'm going to create some very simple classes to use, one with a member function and one with a data member
// using distinc values 7 and 3 to differentiate easily later
struct SizeData {
std::size_t size = 7;
};
struct SizeFunc {
std::size_t size() const { return 3; };
};
I'm going to also write a basic void_t
for metaprogramming, and use a very standard modern metaprogramming approach to check if a given type has a .size()
member function. (it looks like the technique you are attempting is outdated).
template <typename>
using void_type = void;
template <typename T, typename = void>
struct HasSizeFunc : std::false_type { };
template <typename T>
struct HasSizeFunc<T, void_type<decltype(std::declval<const T&>().size())>>
: std::true_type { };
I can use this very easily in a main to check whether something has a .size()
or not
int main() {
std::cout << "SizeFunc: " << HasSizeFunc<SizeFunc>::value << 'n';
std::cout << "SizeData: " << HasSizeFunc<SizeData>::value << 'n';
}
But now for the get_size()
function. As I said earlier, your if/else won't work because both branches aren't compilable (if constexpr
works but isn't available in c++11). So instead you can do what's called "tag dispatch" to decide which overload of a function to call in order to call the right .size
// std::size_t may not be right for every type. leaving it for simplicity.
template <typename T>
std::size_t get_size_impl(T t, std::true_type) {
return t.size();
}
template <typename T>
std::size_t get_size_impl(T t, std::false_type) {
return t.size;
}
template <typename T>
std::size_t get_size(T t) { // note, this should probably be a const reference
// second argument used to select an overload of get_size_impl
return get_size_impl(t, HasSizeFunc<T>{});
}
And to use it:
int main() {
SizeFunc sf;
std::cout << "SizeFunc: " << get_size(sf) << 'n';
SizeData sd;
std::cout << "SizeData: " << get_size(sd) << 'n';
}
click here to see all the code in one live example. I recommend watching these cppcon talks to learn more.
Also, here is what I'd do in c++17
edited Nov 16 '18 at 19:30
answered Nov 16 '18 at 19:23
Ryan Haining
21.2k869121
21.2k869121
1
I like your solution but (my question) what if we have more cases ? I mean here you only assume.size()
and.size
how to handle 3+ case ? eg..length
? We should use what kind of type ? Or maybe use extra template argument likeget_size_impl<0>(t), get_size_impl<1>(t)...
?
– Dawid Drozd
Nov 20 '18 at 11:10
1
@DawidDrozd tag dispatch still works, but you need more than two tags here is an expanded example of the c++11 version. This approach is used by the standard library with different iterators for example, inside something likestd::distance
– Ryan Haining
Nov 20 '18 at 17:05
1
@DawidDrozd also to switch it up for c++17 you can do a similar thing withintegral_constant
andif constexpr
, live example here
– Ryan Haining
Nov 21 '18 at 17:51
add a comment |
1
I like your solution but (my question) what if we have more cases ? I mean here you only assume.size()
and.size
how to handle 3+ case ? eg..length
? We should use what kind of type ? Or maybe use extra template argument likeget_size_impl<0>(t), get_size_impl<1>(t)...
?
– Dawid Drozd
Nov 20 '18 at 11:10
1
@DawidDrozd tag dispatch still works, but you need more than two tags here is an expanded example of the c++11 version. This approach is used by the standard library with different iterators for example, inside something likestd::distance
– Ryan Haining
Nov 20 '18 at 17:05
1
@DawidDrozd also to switch it up for c++17 you can do a similar thing withintegral_constant
andif constexpr
, live example here
– Ryan Haining
Nov 21 '18 at 17:51
1
1
I like your solution but (my question) what if we have more cases ? I mean here you only assume
.size()
and .size
how to handle 3+ case ? eg. .length
? We should use what kind of type ? Or maybe use extra template argument like get_size_impl<0>(t), get_size_impl<1>(t)...
?– Dawid Drozd
Nov 20 '18 at 11:10
I like your solution but (my question) what if we have more cases ? I mean here you only assume
.size()
and .size
how to handle 3+ case ? eg. .length
? We should use what kind of type ? Or maybe use extra template argument like get_size_impl<0>(t), get_size_impl<1>(t)...
?– Dawid Drozd
Nov 20 '18 at 11:10
1
1
@DawidDrozd tag dispatch still works, but you need more than two tags here is an expanded example of the c++11 version. This approach is used by the standard library with different iterators for example, inside something like
std::distance
– Ryan Haining
Nov 20 '18 at 17:05
@DawidDrozd tag dispatch still works, but you need more than two tags here is an expanded example of the c++11 version. This approach is used by the standard library with different iterators for example, inside something like
std::distance
– Ryan Haining
Nov 20 '18 at 17:05
1
1
@DawidDrozd also to switch it up for c++17 you can do a similar thing with
integral_constant
and if constexpr
, live example here– Ryan Haining
Nov 21 '18 at 17:51
@DawidDrozd also to switch it up for c++17 you can do a similar thing with
integral_constant
and if constexpr
, live example here– Ryan Haining
Nov 21 '18 at 17:51
add a comment |
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
these branches are evaluated at runtime. So both branches must be valid at compile time.
#define RETURNS(...)
noexcept(noexcept(__VA_ARGS__))
-> decltype(__VA_ARGS__)
{ return __VA_ARGS__; }
template<class S, class...Ts>
auto select( S, Ts&&...ts )
RETURNS( std::get<S::value>(std::forward_as_tuple( std::forward<Ts>(ts)... )) )
which gives you a compile-time branch.
struct call_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size() )
};
struct get_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size )
};
auto f = select(has_size<T>{},
get_size_t{},
call_size_t{}
};
res = f(t);
this is quite annoying because you are in c++11; code is less than half this in c++14 and becomes trivial in c++17.
add a comment |
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
these branches are evaluated at runtime. So both branches must be valid at compile time.
#define RETURNS(...)
noexcept(noexcept(__VA_ARGS__))
-> decltype(__VA_ARGS__)
{ return __VA_ARGS__; }
template<class S, class...Ts>
auto select( S, Ts&&...ts )
RETURNS( std::get<S::value>(std::forward_as_tuple( std::forward<Ts>(ts)... )) )
which gives you a compile-time branch.
struct call_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size() )
};
struct get_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size )
};
auto f = select(has_size<T>{},
get_size_t{},
call_size_t{}
};
res = f(t);
this is quite annoying because you are in c++11; code is less than half this in c++14 and becomes trivial in c++17.
add a comment |
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
these branches are evaluated at runtime. So both branches must be valid at compile time.
#define RETURNS(...)
noexcept(noexcept(__VA_ARGS__))
-> decltype(__VA_ARGS__)
{ return __VA_ARGS__; }
template<class S, class...Ts>
auto select( S, Ts&&...ts )
RETURNS( std::get<S::value>(std::forward_as_tuple( std::forward<Ts>(ts)... )) )
which gives you a compile-time branch.
struct call_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size() )
};
struct get_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size )
};
auto f = select(has_size<T>{},
get_size_t{},
call_size_t{}
};
res = f(t);
this is quite annoying because you are in c++11; code is less than half this in c++14 and becomes trivial in c++17.
if(has_size<T>::value){
res = t.size();
}else{
res = t.size;
}
these branches are evaluated at runtime. So both branches must be valid at compile time.
#define RETURNS(...)
noexcept(noexcept(__VA_ARGS__))
-> decltype(__VA_ARGS__)
{ return __VA_ARGS__; }
template<class S, class...Ts>
auto select( S, Ts&&...ts )
RETURNS( std::get<S::value>(std::forward_as_tuple( std::forward<Ts>(ts)... )) )
which gives you a compile-time branch.
struct call_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size() )
};
struct get_size_t {
template<class T>
auto operator()( T&& t ) const
RETURNS( t.size )
};
auto f = select(has_size<T>{},
get_size_t{},
call_size_t{}
};
res = f(t);
this is quite annoying because you are in c++11; code is less than half this in c++14 and becomes trivial in c++17.
answered Nov 16 '18 at 19:19
Yakk - Adam Nevraumont
182k19188374
182k19188374
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53343884%2ftry-to-make-meta-function-which-find-method-size-in-class%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Here you have example has_onEnter
– Dawid Drozd
Nov 16 '18 at 19:07
1
In your code probably you want to write get_size(v)
– Dawid Drozd
Nov 16 '18 at 19:09
Thanks! It is my bug...
– Semyon
Nov 18 '18 at 12:13