Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[C++14] So I had some fun with templates and averages...
#1
... and wrote this:
    C++-Code:
template <typename Type,size_t ArraySize>
constexpr size_t size(const Type(&)[ArraySize]){
    return ArraySize;
}
 
template <typename Cont>
//requires std::Container<Cont>
inline auto size(const Cont &cont){
    return cont.size();
}
 
template <typename Cont>
//requires std::Container<Cont>
auto average(const Cont &cont){
    auto Sum = decltype(*std::begin(cont))(0);//deduce the return type, ugly hack, ISO committee please...
 
    for(auto &e:cont) Sum += e;
 
    return Sum/size(cont);
}
 
template <typename Ret,typename Cont>
//requires std::Container<Cont>
Ret average(const Cont &cont){
    Ret Sum = 0;
 
    for(auto &e:cont) Sum += e;
 
    return Sum/size(cont);
}

Basically it is a fully generalized algorithm for calculating the average of the elements within a container, which works on any container type.
The "requires" comments are based on the "Concepts Lite TS", so if it where to be available you'd be able to remove the "//" and the compiler would check that you are only passing containers to the function. I'm not entirely certain whether or not the concepts would belong to the "std" namespace.

I'm just wondering if anyone found this useful, funny, interesting, etc. and if you did please post a reply.
If you have suggestions for the code feel free to post a reply with them. I don't necessarily recommend that all your code looks like this though, as it would quickly get very messy.

Usage:
    C++-Code:
#include <iostream>
#include <vector>
 
using namespace std;
 
int main(){
    int Array[] = {4,2,3,4,8,9,7,2,1};
 
    cout << average(Array) << endl;//prints 4
    cout << average<float>(Array) << endl;//prints "4.444444"
 
    vector<float> Vector = {4.2f,2.3f,8.9f,4.6f,7.8f,6.1f};
    cout << average(Vector) << endl;//prints 5.65
 
    return 0;
}


Edit: Just realized I forgot to write "std::" before "begin" in my code above, so I fixed it.
Age ratings for movies and games (and similar) have never been a good idea.
One can learn a lot from reinventing wheels.
An unsound argument is not the same as an invalid one.
volatile in C++ does not mean thread-safe.
Do not make APIs unnecessarily asynchronous.
Make C++ operator > again
Trump is an idiot.
Reply
Thanks given by: A-Man
#2
At first, I didn't really know what half of the code was doing (rarely did I even get past C++98, let alone 14), so I had to do my reading before I comment.

The idea of having functions be as flexible as that is rather brilliant, and I can see myself adopting it :P.
The compiler did throw 2 errors though (about not able to figure out the return types for "auto" of the average and size functions). Using "-> decltype(returntype)" after each function's declaration fixed the problem.
    CPP-Code:
template <typename Cont>
//requires std::Container<Cont>
inline auto size(const Cont &cont) -> decltype(cont.size())
{
    return cont.size();
}
 
template <typename Cont>
//requires std::Container<Cont>
auto average(const Cont &cont) -> decltype(*begin(cont))
{
    auto Sum = decltype(*begin(cont))(0);//deduce the return type, ugly hack, ISO committee please...
 
    for(auto &e:cont) Sum += e;
 
    return Sum/size(cont);
}


I can't understand what exactly did you do there for the new size() function.

(I find that hack to be kind of neat btw)
[Image: signature.png]
A-Engine: A new beat em up game engine inspired by LF2. Coming soon

A-Engine Dev Blog - Update #8: Timeout

Reply
Thanks given by:
#3
(01-28-2015, 08:26 AM)Doctor A Wrote:  The idea of having functions be as flexible as that is rather brilliant, and I can see myself adopting it :P.
Be careful with that thinking. Just getting these to work took a fair bit of effort, and it is a simple compute average function. If you are working with something more complex than this then you'll probably end up taking more time than you really should by making it general.

(01-28-2015, 08:26 AM)Doctor A Wrote:  The compiler did throw 2 errors though (about not able to figure out the return types for "auto" of the average and size functions). Using "-> decltype(returntype)" after each function's declaration fixed the problem.
That won't work for the second function, as "decltype(*begin(cont))" will be deduced to "const T&", and as a result you'd be returning a reference to a local object, leading you on the path to undefined behavior.
The reason the compiler doesn't do this properly is because it doesn't support return type deduction for functions, which this code depends on. What compiler are you using?

(01-28-2015, 08:26 AM)Doctor A Wrote:  I can't understand what exactly did you do there for the new size() function.
The first variant simply figures out what the size of an array is, so:
    C++-Code:
int Array[10];
cout << size(Array) << endl;//prints 10

The constexpr means that the result can and should be computed during compile-time, so the above example is literally the same as writing:
    C++-Code:
cout << 10 << endl;//prints 10

So no time is spent during run time trying to find a value the compiler already knows.

The second function basically calls the member function "size()" on whatever is passed to it:
    C++-Code:
vector<int> Vec = {1,2,3,4,5,6,7,8,9};
cout << size(Vec) << endl;
//the previous line is literally the same as writing:
cout << Vec.size() << endl;

The "size()" member function is what is used to get the size of all standard containers.

There are two reasons for using a non member size function:
  • It works for arrays as it has a special variant for them.
  • It works for any container you can come up with, regardless of how you retrieve the size of the container.
    In order for that to work you have to write a template specialization:
        C++-Code:
    class MyContainer {
        public:
            int GetSize() const {return 123;}
    };
     
    template<>
    auto size<MyContainer>(const MyContainer &Container){
        return Container.GetSize();
    }
    //...
    MyContainer c;
    cout << size(c) << endl;//prints 123

Age ratings for movies and games (and similar) have never been a good idea.
One can learn a lot from reinventing wheels.
An unsound argument is not the same as an invalid one.
volatile in C++ does not mean thread-safe.
Do not make APIs unnecessarily asynchronous.
Make C++ operator > again
Trump is an idiot.
Reply
Thanks given by: A-Man
#4
Quote:That won't work for the second function, as "decltype(*begin(cont))" will be deduced to "const T&", and as a result you'd be returning a reference to a local object, leading you on the path to undefined behavior.
Aha! So basically I was lucky the program didn't choose to use that part of the memory again.
I was wondering also, would having a reference argument to a function declared "const" cause the compiler to create a whole new instance which is read only? Or would it just lock the argument for the function's scope? (I know I can just try it out myself, but on the way :P)

I use GNU GCC compiler on code::blocks (isn't that what you're using?).
[Image: signature.png]
A-Engine: A new beat em up game engine inspired by LF2. Coming soon

A-Engine Dev Blog - Update #8: Timeout

Reply
Thanks given by:
#5
It'll probably be too old. https://gcc.gnu.org/projects/cxx1y.html Most of the stuff is implemented in 4.9+.
[Image: doty7Xn.gif]

10 ʏᴇᴀʀs sɪɴᴄᴇ ɪʀᴄ ɢᴏᴏᴅ.ɪ ᴡᴀʟᴋ ᴛʜʀᴏᴜɢʜ ᴛʜᴇ ᴇᴍᴘᴛʏ sᴛʀᴇᴇᴛs ᴛʀʏɪɴɢ ᴛᴏ ᴛʜɪɴᴋ ᴏғ sᴏᴍᴇᴛʜɪɴɢ ᴇʟsᴇ ʙᴜᴛ ᴍʏ ᴘᴀᴛʜ ᴀʟᴡᴀʏs ʟᴇᴀᴅs ᴛᴏ ᴛʜᴇ ɪʀᴄ. ɪ sᴛᴀʀᴇ ᴀᴛ ᴛʜᴇ sᴄʀᴇᴇɴ ғᴏʀ ʜᴏᴜʀs ᴀɴᴅ ᴛʀʏ ᴛᴏ sᴜᴍᴍᴏɴ ᴛʜᴇ ɢᴏᴏᴅ ɪʀᴄ. ɪ ᴡᴀᴛᴄʜ ᴏᴛʜᴇʀ ɪʀᴄ ᴄʜᴀɴɴᴇʟs ʙᴜᴛ ɪᴛ ɪs ɴᴏ ɢᴏᴏᴅ. ɪ ᴘᴇsᴛᴇʀ ᴢᴏʀᴛ ᴀɴᴅ ᴛʀʏ ᴛᴏ ʀᴇsɪsᴛ ʜɪs sᴇxɪɴᴇss ʙᴜᴛ ɪᴛ ɪs ᴀʟʟ ᴍᴇᴀɴɪɴɢʟᴇss. ᴛʜᴇ ᴇɴᴅ ɪs ɴᴇᴀʀ.ɪ ᴛʜᴇɴ ᴜsᴜᴀʟʟʏ ʀᴇᴀᴅ sᴏᴍᴇ ᴏʟᴅ ɪʀᴄ ʟᴏɢs ᴀɴᴅ ᴄʀʏ ᴍʏsᴇʟғ ᴛᴏ sʟᴇᴇᴘ.


Reply
Thanks given by:
#6
(01-28-2015, 02:41 PM)Doctor A Wrote:  Aha! So basically I was lucky the program didn't choose to use that part of the memory again.
Actually, the program could have ordered you a burrito, formatted your harddrive and/or built and launched a spaceship to mars :P (though all of those would have been extremely unlikely)

(01-28-2015, 02:41 PM)Doctor A Wrote:  I was wondering also, would having a reference argument to a function declared "const" cause the compiler to create a whole new instance which is read only? Or would it just lock the argument for the function's scope? (I know I can just try it out myself, but on the way :P)
If you are talking about the following:
    C++-Code:
void MyFunction(const string&String);
string MyString = "Hello world!";
MyFunction(MyString);

Then a reference to the original string (MyString) will be passed to the function, so no new string object will be created. You could say that the object is const in the functions scope, but there is nothing preventing anything else from modifying it. (and if you are willing to use casts, then there is also nothing preventing the function itself from modifying it)

(01-28-2015, 02:41 PM)Doctor A Wrote:  I use GNU GCC compiler on code::blocks (isn't that what you're using?).
I use an updated GNU GCC Compiler :P.
This one: http://mingw-w64.sourceforge.net/
Age ratings for movies and games (and similar) have never been a good idea.
One can learn a lot from reinventing wheels.
An unsound argument is not the same as an invalid one.
volatile in C++ does not mean thread-safe.
Do not make APIs unnecessarily asynchronous.
Make C++ operator > again
Trump is an idiot.
Reply
Thanks given by: A-Man




Users browsing this thread: 1 Guest(s)