How do I call template array operator overloading function? - c++

I need to create an adapter C++ class, which accepts an integer index, and retrieves some types of data from a C module by the index, and then returns it to the C++ module.
The data retrieving functions in the C module are like:
int getInt(int index);
double getDouble(int index);
const char* getString(int index);
// ...and etc.
I want to implement an array-like interface for the C++ module, so I created the following class:
class Arguments {
public:
template<typename T> T operator[] (int index);
};
template<> int Arguments::operator[] (int index) { return getInt(index); }
template<> double Arguments::operator[] (int index) { return getdouble(index); }
template<> std::string Arguments::operator[] (int index) { return getString(index); }
(Template class doesn't help in this case, but only template member functions)
The adapter class is no biggie, but calling the Arguments::operator[] is a problem!
I found out that I can only call it in this way:
Arguments a;
int i = a.operator[]<int>(0); // OK
double d = a.operator[]<double>(1); // OK
int x = a[0]; // doesn't compile! it doesn't deduce.
But it looks like a joke, doesn't it?
If this is the case, I would rather create normal member functions, like template<T> T get(int index).
So here comes the question: if I create array-operator-overloading function T operator[]() and its specializations, is it possible to call it like accessing an array?
Thank you!

The simple answer is: No, not possible. You cannot overload a function based on its return type. See here for a similar quesiton: overload operator[] on return type
However, there is a trick that lets you deduce a type from the lhs of an assignment:
#include <iostream>
#include <type_traits>
struct container;
struct helper {
container& c;
size_t index;
template <typename T> operator T();
};
struct container {
helper operator[](size_t i){
return {*this,i};
}
template <typename T>
T get_value(size_t i){
if constexpr (std::is_same_v<T,int>) {
return 42;
} else {
return 0.42;
}
}
};
template <typename T>
helper::operator T(){
return c.get_value<T>(index);
}
int main() {
container c;
int x = c[0];
std::cout << x << "\n";
double y = c[1];
std::cout << y ;
}
Output is:
42
0.42
The line int x = c[0]; goes via container::get_value<int> where the int is deduced from the type of x. Similarly double y = c[1]; uses container::get_value<double> because y is double.
The price you pay is lots of boilerplate and using auto like this
auto x = c[1];
will get you a helper, not the desired value which might be a bit unexpected.

Related

How to use declare a function template pointer typedef without specifying template?

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
enum Op{ADD, SUB, MUL, DIV, MATMUL};
template <typename dtype>
using AlgoFunction = double(*)(const vector<dtype> &, Op);
// for example, the sum function doesn't require template.
// just write sum(a), not sum<float>(a)
template <typename dtype>
double sum(vector<dtype> inputs) {
dtype summer = inputs[0];
for (int i=1; i<inputs.size(); i++) summer = summer + inputs[i];
return double(summer);
}
// i need to do ask this question because I perform the same
// algorithm (linearAlgo, ...) on different types of data
// (dtype = float, double, matrix<float>, matrix<double>, ...
template <typename dtype>
inline dtype numOperate(const dtype &a, const dtype &b, Op op) {
if (op==ADD) return a + b;
if (op==SUB) return a - b;
if (op==MUL) return a * b;
if (op==DIV) return a / b;
}
template <typename dtype>
double linearAlgo(const vector<dtype> &inputs, Op op) {
dtype summer = inputs[0];
for (int i=1; i<inputs.size(); i++) summer = numOperate(summer, inputs[i], op);
return double(summer);
}
template <typename dtype>
double reverseLinearAlgo(const vector<dtype> &inputs, Op op) {
int n = inputs.size();
dtype summer = inputs[n-1];
for (int i=n-2; i>=0; i--) summer = numOperate(summer, inputs[i], op);
return double(summer);
}
template<typename dtype>
vector<double> run(vector<dtype> inputs, Op op, double (*func)(const vector<dtype>&, Op)) {
vector<double> res;
res.push_back(func(inputs, op));
return res;
}
int main()
{
vector<float> a;
vector<double> b;
a.push_back(1); a.push_back(2); a.push_back(3);
b.push_back(1); b.push_back(2); b.push_back(3);
vector<double> res = run(a, ADD, linearAlgo); // allowed without specifying template
vector<double> resf = run(b, ADD, linearAlgo); // still work with multiple data type
// I want to do this assignment without specifying the template.
// in the above linear, linearAlgo (no specifying template) is possible, why not here ?
AlgoFunction<float> functor = reverseLinearAlgo; // works, but I don't want it
//AlgoFunction functor = reverseLinearAlgo; // I want to do this. compile error
vector<double> res2 = run(a, ADD, functor);
cout << res[0] << "\n";
cout << res2[0];
return 0;
}
So I have a function template pointer
template <typename dtype>
using AlgoFunction = double(*)(const vector<dtype> &, Op);
that points to functions like this
template <typename dtype>
double linearAlgo(const vector<dtype> &inputs, Op op) {
dtype summer = inputs[0];
for (int i=1; i<inputs.size(); i++) summer = numOperate(summer, inputs[i], op);
return double(summer);
}
I know that using a template function pointer without specifying template is possible. For example:
vector<float> a;
a.push_back(1); a.push_back(2); a.push_back(3);
vector<double> res = run(a, ADD, linearAlgo); // allowed without specifying template
But then if I declare a variable of type AlgoFunction, the compiler force me to specify the template.
//AlgoFunction<float> functor = reverseLinearAlgo; // works, but I don't want it
AlgoFunction functor = reverseLinearAlgo; // I want to do this. compile error
This is not good because I have many types of data dtype, and I don't want to specify the template again for each one.
So how can I declare AlgoFunction functor; instead of AlgoFunction<some_datatype_name> functor; ?
Thank you.
Edit: the goal is to have a vector<AlgoFunction> functors instead of vector<AlgoFunction<data_type> >. Since in the example, res, and resf both can be calculated without specifying the template for the 3rd parameter, I want to know if vector<AlgoFunction> is possible or not.
You cannot. I suspect the confusion stems from missing the difference between a "function" and a "function template".
To explain why your first example worked, first let's examine what is actually happening when you do run(a, ADD, linearAlgo);. As a reminder, we have:
template <typename dtype>
using AlgoFunction = double(*)(const std::vector<dtype>&, Op);
template <typename dtype>
std::vector<double> run(const std::vector<dtype>&, Op,
double(*)(const std::vector<dtype>&, Op));
Equivalently, we could have had the following:
std::vector<double> run(const std::vector<dtype>&, Op, AlgoFunction<dtype>);
since AlgoFunction is just an alias.
Now, when we do this:
std::vector<double> a;
run(a, ADD, linearAlgo);
we know that the first argument to run, std::vector<dtype>, is std::vector<double>, and hence dtype is double. We can't determine anything about dtype from the third argument since linearAlgo is just a template, a "pattern".
Since we know that dtype must be double, we can choose and instantiate linearAlgo<dtype> – that is, linearAlgo<double> – as our function since that fits our signature, and everything is OK.
Now, what does that have to do with this?
AlgoFunction functor = reverseLinearAlgo;
In this case, we're trying to create a variable. reverseLinearAlgo is just a function template, not an actual function, and we don't have any other context to determine what type functor actually is. Hence the compiler error.
Moreover, what would this actually mean? Would functor have a different type depending on where you used it? If I did auto x = functor;, what type would x have? If I did something like
AlgoFunction functor = reverseLinearAlgo;
if (test) {
std::vector<float> x;
functor(x, ADD);
} else {
std::vector<double> x;
functor(x, ADD);
}
would that mean that functor has dynamic type? This isn't something that works with C++'s (static) type system, and it can quickly get out of hand if this was made legal. This is the case with your wish for std::vector<AlgoFunction>: you have to store a concrete type. Otherwise the program will need to dynamically instantiate a function based on runtime information: template parameters must be known at compile time.
One possible alternative, if you know the types ahead of time, is to use a std::variant of the possible types you might instantiate with. That is, something like
std::vector<std::variant<AlgoFunction<float>, AlgoFunction<double>>>;
if each element of the vector should provide one or the other, or else use
std::vector<std::tuple<AlgoFunction<float>, AlgoFunction<double>>>;
if each element of the vector should be usable with either type.
Whether this is useful, and worth the added complexity, is up to you.
It's possible to do what you want, but it is messy to implement in C++ because you have to do manual type checking if you want to seriously implement something like this.
Here is a quick method to do what you want, but beware that you need more than this to make something useful for serious work, and it's extremely easy to shoot yourself in the foot with this kind of code:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct AlgoFunction {
virtual double operator()(void *) = 0;
};
template <class T>
struct AF_Sum : public AlgoFunction {
virtual double operator()(void * inputVec) {
T res = T();
vector<T>* pInput = (vector<T>*)inputVec;
for (int i = 0; i < pInput->size(); ++i) {
res += (*pInput)[i];
}
return (double) res;
}
};
template <class T>
struct AF_Mean : public AlgoFunction {
virtual double operator()(void * inputVec) {
T res = T();
vector<T>* pInput = (vector<T>*)inputVec;
for (int i = 0; i < pInput->size(); ++i) {
res += (*pInput)[i];
}
return (double) res / (double)pInput->size();
}
};
int main()
{
std::vector<float> vF{0.2, 0.3, 0.8};
std::vector<int> vI{2, 5, 7};
std::vector<AlgoFunction*> algoFunctions;
algoFunctions.push_back(new AF_Sum<float>);
algoFunctions.push_back(new AF_Mean<int>);
cout << (*algoFunctions[0])(&vF) << endl;
cout << (*algoFunctions[1])(&vI) << endl;
return 0;
}
Notice that I didn't bother cleaning the heap-allocated memory (via new) and I didn't implement all of your functions; just a quick and dirty example of a potential solution.

Typecast operator overload with as nonmember function

Is it possible to define the typecast operator as a non-member global
function?
#include <stdio.h>
#include <stdlib.h>
typedef int hash_t;
struct wrap {
int v;
operator hash_t () {
hash_t h = v+1;
return h;
}
};
int main(int argc, char **argv) {
int v = 1;
hash_t h = (hash_t)wrap{v};
printf("%d\n", h);
return 0;
}
Instead of (hash_t)wrap{v} being able to write (hash_t)v would be helpful. Not shure how the syntax of the overload would be, but something like operator hash_t (int v) { ... } ? Or is the typecast operator only overloadable in with memberfunction of a class/struct?
I want to define a dictionary template <typename key> CDict and want to convert key to int to use it as a hash. But I dont want to dispatch the hash function depending on the type of key or use a virtual function and also be able to use int as type for key.... Instead I want to overload the (hash_t) typecast.
Is this possible with global typecast overloads (or dont they exits) ?
It seems what you actually want to do is call different functions depending on the type, the operator overload is not the right way to go about this.
Instead, you can create a free function to handle int specifically, and then delegate to a member function in the non-int case
using hash_type = int;
// handle int
hash_type my_hash(int i) {
return i;
}
// handle anything else
template <typename T>
hash_type my_hash(const T& t) {
return t.hash(); // use whatever pattern you want here
}
template <typename Key, typename Value>
struct CDict {
void insert(const Key& k, const Value& v) {
auto hash = my_hash(k); // calls correct overload
}
};
// a type with a hash member function
struct MyHashable {
hash_type hash() const {
return 0;
}
};
int main() {
CDict<int, double> c1;
c1.insert(3, 5.0);
CDict<MyHashable, char> c2;
c2.insert(MyHashable{}, 'a');
}
Don't create type names ending with _t, they are reserved

Virtually turn vector of struct into vector of struct members

I have a function that takes a vector-like input. To simplify things, let's use this print_in_order function:
#include <iostream>
#include <vector>
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<int> printme = {100, 200, 300};
std::vector<int> order = {2,0,1};
print_in_order(order, printme);
}
Now I have a vector<Elem> and want to print a single integer member, Elem.a, for each Elem in the vector. I could do this by creating a new vector<int> (copying a for all Elems) and pass this to the print function - however, I feel like there must be a way to pass a "virtual" vector that, when operator[] is used on it, returns this only the member a. Note that I don't want to change the print_in_order function to access the member, it should remain general.
Is this possible, maybe with a lambda expression?
Full code below.
#include <iostream>
#include <vector>
struct Elem {
int a,b;
Elem(int a, int b) : a(a),b(b) {}
};
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<Elem> printme = {Elem(1,100), Elem(2,200), Elem(3,300)};
std::vector<int> order = {2,0,1};
// how to do this?
virtual_vector X(printme) // behaves like a std::vector<Elem.a>
print_in_order(order, X);
}
It's not really possible to directly do what you want. Instead you might want to take a hint from the standard algorithm library, for example std::for_each where you take an extra argument that is a function-like object that you call for each element. Then you could easily pass a lambda function that prints only the wanted element.
Perhaps something like
template<typename vectorlike, typename functionlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme,
functionlike func) {
for (int i : order)
func(printme[i]);
}
Then call it like
print_in_order(order, printme, [](Elem const& elem) {
std::cout << elem.a;
});
Since C++ have function overloading you can still keep the old print_in_order function for plain vectors.
Using member pointers you can implement a proxy type that will allow you view a container of objects by substituting each object by one of it's members (see pointer to data member) or by one of it's getters (see pointer to member function). The first solution addresses only data members, the second accounts for both.
The container will necessarily need to know which container to use and which member to map, which will be provided at construction. The type of a pointer to member depends on the type of that member so it will have to be considered as an additional template argument.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
private:
const Container * m_container;
MemberPtr m_member;
};
Next, implement the operator[] operator, since you mentioned that it's how you wanted to access your elements. The syntax for dereferencing a member pointer can be surprising at first.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// Dispatch to the right get method
auto operator[](const size_t p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To use this implementation, you would write something like this :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
virtual_vector<decltype(printme), decltype(&Elem::a)> X(printme, &Elem::a);
print_in_order(order, X);
}
This is a bit cumbersome since there is no template argument deduction happening. So lets add a free function to deduce the template arguments.
template<class Container, class MemberPtr>
virtual_vector<Container, MemberPtr>
make_virtual_vector(const Container & p_container, MemberPtr p_member_ptr)
{
return{ p_container, p_member_ptr };
}
The usage becomes :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
If you want to support member functions, it's a little bit more complicated. First, the syntax to dereference a data member pointer is slightly different from calling a function member pointer. You have to implement two versions of the operator[] and enable the correct one based on the member pointer type. Luckily the standard provides std::enable_if and std::is_member_function_pointer (both in the <type_trait> header) which allow us to do just that. The member function pointer requires you to specify the arguments to pass to the function (non in this case) and an extra set of parentheses around the expression that would evaluate to the function to call (everything before the list of arguments).
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// For mapping to a method
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == true, const size_t> p_index) const
{
return ((*m_container)[p_index].*m_member)();
}
// For mapping to a member
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == false, const size_t> p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To test this, I've added a getter to the Elem class, for illustrative purposes.
struct Elem {
int a, b;
int foo() const { return a; }
Elem(int a, int b) : a(a), b(b) {}
};
And here is how it would be used :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
{ // print member
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
{ // print method
auto X = make_virtual_vector(printme, &Elem::foo);
print_in_order(order, X);
}
}
You've got a choice of two data structures
struct Employee
{
std::string name;
double salary;
long payrollid;
};
std::vector<Employee> employees;
Or alternatively
struct Employees
{
std::vector<std::string> names;
std::vector<double> salaries;
std::vector<long> payrollids;
};
C++ is designed with the first option as the default. Other languages such as Javascript tend to encourage the second option.
If you want to find mean salary, option 2 is more convenient. If you want to sort the employees by salary, option 1 is easier to work with.
However you can use lamdas to partially interconvert between the two. The lambda is a trivial little function which takes an Employee and returns a salary for him - so effectively providing a flat vector of doubles we can take the mean of - or takes an index and an Employees and returns an employee, doing a little bit of trivial data reformatting.
template<class F>
struct index_fake_t{
F f;
decltype(auto) operator[](std::size_t i)const{
return f(i);
}
};
template<class F>
index_fake_t<F> index_fake( F f ){
return{std::move(f)};
}
template<class F>
auto reindexer(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return v[f(i)];
});
};
}
template<class F>
auto indexer_mapper(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return f(v[i]);
});
};
}
Now, print in order can be rewritten as:
template <typename vectorlike>
void print(vectorlike const & printme) {
for (auto&& x:printme)
std::cout << x << std::endl;
}
template <typename vectorlike>
void print_in_order(std::vector<int> const& reorder, vectorlike const & printme) {
print(reindexer([&](auto i){return reorder[i];})(printme));
}
and printing .a as:
print_in_order( reorder, indexer_mapper([](auto&&x){return x.a;})(printme) );
there may be some typos.

Passing function to template object when initializing template in C++

I'm trying to write an implementation for hash map, I'm not allowed to use anything from stdlib except for iostream, string and cassert.
It needs to be generic, so the values that populate the buckets can be of any type. I need templates for this, but can't manage to pass the hash function in any way. This would be the header file:
template<typename Value, typename hashFunction>
class hashTable{
public:
hashTable(int size){
//Creates an empty vector of size on the table
}
define(Value v){
loads value in Vector[hashFunction(v)];
}
...
private:
Vector with all the elements
}
Note: I guess I don't need templates for the keys, do I?
I can't define the hash function inside my class because I'd have to make one that works with all types (string to int, int to int, double to int, etc). So I guess the only solution is to pass the function as argument in my main. This would be the main.
int hashF(int v){return v}
int main(){
hashTable<int,int,hashF> table(5);
}
But this doesn't work, g++ tells me "expected type but got hashF". I guess I could pass a pointer to a function, but that seems like a hack rather than a real solution. Is there a better way?
template<typename Value, int(*fun)(Value)>
class hashTable {
std::vector<Value> v;
public:
hashTable(std::size_t size) : v(size) { }
void define(Value &&val) { v[fun(val)] = val; }
};
Live Demo
Non function pointer way:
template<typename Value, typename F>
class hashTable {
std::vector<Value> v;
F fun;
public:
hashTable(std::size_t size, F fun_) : v(size), fun(fun_) { }
void define(Value &&val) { v[fun(val)] = val; }
};
Live Demo
Managed to get it working with Neil's advice. My hash.h:
template<typename C, typename D, typename H>
class Tabla {
public:
Tabla(int s){
cout << hashF(3) << endl;
size=s;
}
private:
H hashF;
int size;
};
My hash.cpp
struct KeyHash {
unsigned long operator()(const int& k) const
{
return k % 10;
}
};
int main(){
Tabla<int,int,KeyHash> tab(3);
return 0;
}
This example is just to show I'm able to use the function inside the template, then I'd have to code the define and delete functions that use that KeyHash.
Dunno why I have to wrap it like this, but it works. Found the specifics of it here

Initialise a std::vector with data from an array of unions

How might I initialise a std::vector from an array of structs, where the struct contains a union of different types. In other words, the array is used to store a number of values of a specific type, which can be int, char* etc.
This is my solution so far but I'm looking for a better approach:
The convert function returns a vector<int> if it stores ints or a vector<std::string> if it stores char*.
The Value type below is a struct containing a union called value. The Container class below points to a buffer of such Values.
// union member getter
class Getter
{
public:
void operator()(int8_t& i, const Value& value)
{
i = value.value.i;
}
void operator()(std::string& s, const Value& value)
{
s = std::string(value.value.s);
}
...
};
template<class T>
std::vector<T> convert(Container* container)
{
std::vector<T> c;
c.reserve(container->nrOfValues);
Getter g;
for(int i=0;i<container->nrOfValues;i++)
{
T value;
g(value, container->values[i]);
c.push_back(value);
}
return c;
}
Your problem is the union gives a different name to each value, which causes the need for a function that converts a name to a type, such as Getter::operator() returning a type and getting a named member of the union.
There isn't much you can do with this. You can save a variable declaration and a copy/string constructor on each item, but that's about it.
If you can't modify the original struct, you could initialize the vector with a length set of default value (which must be passed in), then iterate through using the getter as:
vector<T> v(length, defaultValue);
typename vector<T>::iterator iter = vec.begin();
for(int index = 0; *iter != vec.end() && index < length; ++iter, ++index) {
converter(*iter, array[index]);
}
Notice that this starts getting cumbersome in iterating the index and the iterator and verifying both are still valid in case of an accident...
If you can modify the original struct:
class Ugly { // or struct, it doesn't matter
public:
union {
char* s;
int i;
} value;
Ugly(char* s) {
value.s = s;
}
Ugly (const int& i) {
value.i = i;
}
operator std::string() const {
return std::string(value.s);
}
operator int() const {
return value.i;
}
};
Then your for loop becomes:
for(int i=0;i<container->nrOfValues;i++)
{
c.push_back(container->values[i]);
}
Note: You might create the vector and pass it as an argument to the copy function since it involves copying the data over during the return.
If you like some template magic, you could do it slightly different way:
// Source union to get data from
union U
{
int i;
char* s;
double d;
};
// Conversion type template function (declared only)
template <class T> T convert(const U& i_u);
// Macro for template specializations definition
#define FIELD_CONV(SrcType, DestField)\
template <> SrcType convert(const U& i_u)\
{ auto p = &DestField; return i_u.*p; }
// Defining conversions: source type -> union field to get data from
FIELD_CONV(int, U::i)
FIELD_CONV(std::string, U::s)
FIELD_CONV(double, U::d)
// Get rid of macro that not needed any more - just for macro haters ;-)
#undef FIELD_CONV
// Usage
template<class T> std::vector<T> convert(Container* container)
{
std::vector<T> c;
c.reserve(container->nrOfValues);
for(int i = 0; i < container->nrOfValues; ++i)
c.push_back(convert<T>(container->values[i]));
return c;
}
The advantage of this approach - it is short, simple and easy to extend. When you add new field to union you just write another FIELD_CONV() definition.
Compiled example is here.