I have the following simplified code where the compiler is not recognizing the data types and I am not sure why. In the code, I want to be able to pass a map, where the keywords can either be a std::string, a char, or an int. I also want the user to be able to choose whether the associated value is a float or double. In addition, based on the type of the associated value I want the return to either be std::vector<float> or std::vector<double>. Due to the variability in data types, I choose to code this problem as a template.
// main.cpp
#include "test.hpp"
#include <iostream>
#include <math.h>
#include <map>
#include <tuple>
double func6(std::map<char, double> arr);
int main(int argc, const char * argv[]) {
std::map<char, double> inputs;
inputs['x'] = 2.0;
inputs['y'] = 5.438;
std::tuple<std::vector<double>, std::vector<double>> answer;
ODESolver q;
answer = q.ode_solver(inputs, func6);
// - The line below this was tried and it did not work
// any better than the line of code above this.
// answer = q.ode_solver<char, double>(inputs, func6);
return 0;
}
double func6(std::map<char, double> arr)
{
return arr['y'] * log(arr['y']) / arr['x'];
}
The .hpp file has the following information.
#ifndef test_hpp
#define test_hpp
#include <stdio.h>
#include <tuple>
#include <vector>
#include <map>
#include <functional>
class ODESolver
{
public:
template<class char_type, class real_type>
static inline std::tuple<std::vector<real_type>, std::vector<real_type>>
ode_solver(std::map<char_type, real_type> &inputs,
const std::function<real_type(std::map<char_type, real_type>)>& func)
{
// - This function does not work with the function call
// as written
// - The code in this function is irrelevant, it was just
// created to have returnable information of the correct
// type to test the function call
std::vector<real_type> one = {0.0, 1.0};
std::vector<real_type> two = {0.0, 1.0};
std::tuple<std::vector<real_type>, std::vector<real_type>> three(one, two);
return three;
}
};
#endif /* test_hpp */
The compiler does not allow the template shown above, because it does not recognize a matching function call for answer= q.ode_solver(inputs, func6) in the main program. However, if I replace the std::function statement with the following code it works fine.
template<class char_type, class real_type>
static inline std::tuple<std::vector<real_type>, std::vector<real_type>>
ode_solver(std::map<char_type, real_type> &inputs,
const std::function<double(std::map<char, double>)>& func)
All I am doing is replacing the templated parameters with the parameters I want to use in this exact situation, but that defeats the reason for using a template. I have also tried calling the function with answer = q.ode_solver<char, double>(inputs, func6); and it still does not recognize the function. What am I missing?
Implicit conversions (from the function pointer to std::function in this case) won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
You can add an explicit conversion, e.g.
answer = q.ode_solver(inputs, static_cast<std::function<double(std::map<char, double>)>>(func6));
or specify the template arguments explicitly (I'm not sure why you said it didn't work, see the live demo linked below)
answer = q.ode_solver<char, double>(inputs, func6);
LIVE
Or just add another template parameter instead of using std::function.
template<class char_type, class real_type, class F>
static inline std::tuple<std::vector<real_type>, std::vector<real_type>>
ode_solver(std::map<char_type, real_type> &inputs,
const F& func)
Related
I was trying to remove some redundant code from one of my personal projects and got an idea to dynamically create some properties, so the code is the following
#include <functional>
#include <string>
#include <variant>
struct t {
void i(int) {}
void s(std::string) {}
void b(bool) {}
};
int main() {
using setters = std::variant<std::function<void(t *, int)>,
std::function<void(t *, std::string)>,
std::function<void(t *, bool)>>;
std::initializer_list<std::pair<const char *, setters>> props{
{"first", std::function<void(t*, int)>(&t::i)}, {"second", &t::i}};
int i = 0;
for(auto& p : props) {
if (std::get<std::function<void(t*, int)>>(p.second))
i += 2;
}
return i;
}
this actually fails to compile but if I replace &t::i with &t::s for second it actually compiles fine and also if I implicitly specify the type of second it compiles fine also, but I don't like to specify types everywhere, so is there any way to fix it and also it will not compile if I remove the type of first, so is there any way to remove the type specification of first as well
Here is the godbolt link if anyone wants
https://godbolt.org/z/GSb8CL
I'm still working on getting my first template to work in C++. I'm creating a probability class that contains a template method for calculating the average of a given array. I have this template set up such that the array type and size are flexible.
However, I keep getting a compiler error stating that the array type is not being specified.... which has got me thinking 'Well duh'. I can't seem to fix this issue. I'm not sure whether or not I have a compiler setting misconfigured, or if I'm a missing a semicolon somewhere.
My code is posted below:
Probability Header
#ifndef COFFEEDEVMATH_PROBABILITY_H
#define COFFEEDEVMATH_PROBABILITY_H
class Probability
{
public:
Probability(void);
template <typename T, std::size_t N>
void ExpectedValueDataSet(const std::array<T, N>& data)
{
// Do nothing (place holder)
}
protected:
private:
};
#endif // COFFEEDEVMATH_PROBABILITY_H
Main
#include <iostream>
#include <Probability.h>
int main()
{
std::array<float, 4> hoor = {4, 4, 1, 1};
Probability prob;
//prob.ExpectedValueDataSet(hoor);
}
Just put a #include<array> directive in the file header and, for the sake of clarity, in the main.cpp (even if the second one wouldn't be strictly necessary in this case).
With the below code:
materia.h:
#ifndef MATERIA_H
#define MATERIA_H
class material
{
public:
template <class type>
static material* MakeMaterial(typename type::configtype, long);
template <class type>
void CreateNaturalForm(typename type::configtype, long);
…
};
template <class type>
material* material::MakeMaterial(typename type::configtype Config, long Volume)
{
return type::Spawn(Config, Volume);
}
#endif
materias.h:
#ifndef MATERIAS_H
#define MATERIAS_H
#include "materia.h"
#include "confdef.h"
class solid : public material {
public:
typedef solidmaterial configtype;
…
};
template material* material::MakeMaterial<solid>(solidmaterial, long);
template <class type>
void material::CreateNaturalForm(typename type::configtype Config, long Volume)
{
…
MakeMaterial(Config, Volume); // Error here
…
}
template void material::CreateNaturalForm<solid>(solidmaterial, long);
#endif
confdef.h:
#ifndef CONFDEF_H
#define CONFDEF_H
enum solidmaterial {
WOOD,
…
};
#endif
main.cpp
#include "materia.h"
#include "materias.h"
#include "confdef.h"
int main()
{
material::MakeMaterial(WOOD, 500); // Same error here
}
(Here's an online version of the above code that reproduces the error.)
I get the following compilation error message on the commented line:
No matching function for call to 'MakeMaterial'
What am I doing wrong? Shouldn't the explicit instantiation allow the compiler to see the correct function?
The code compiles if I write MakeMaterial<solid> explicitly, but the whole point here is to deduce type from the Config argument. How can I achieve this?
In the call
MakeMaterial(Config, Volume); // Error here
the compiler is asked to find a match where type::configtype in the function template, is the type of Config.
But nothing tells the compiler what to match type to: this is not an explicit instantiation.
In general there could be hundreds of types that type could be matched to, where type::configtype would be the type of Config. C++ does not support the special case where there is only one such possible type.
How to fix that depends on what you meant to accomplish.
I am trying to do some overload on the template function, following are the examples
do_something.h
template<typename T>
void do_something(T const &input){/*....*/}
void do_something(std::string const &input);
void do_something(boost::container::string const &input);
so far, so good, but what if I want to overload a non-defined type?
like using a type some_type have not defined in the header file
void do_something(some_type const &input);
I want to use it like this
main.cpp
#include "do_something.h"
#include "some_type.h"
#include <boost/container/string.hpp>
int main()
{
do_something(std::string("whatever"));
do_something(boost::container::string("whatever"));
//oops, some_type() never defined in the header file, this
//function will call the template version, but this is not
//the behavior user expected
do_something(some_type());
}
Since some_type is not a POD, not a std::string, boost::container::string.I guess I could designed a traits to do some compile time checking
template<typename T>
typename boost::enable_if<is_some_type<T>::value, T>::type
do_something(T const &input){//.....}
But do I have a better way to do it?
I need compile time type checking, so I use template.All of the types calling this function will do similar jobs based on different types, so I prefer overload.I do not need to save the state, so I prefer function rather than class.
Hope this could help you know more about what I intent to do.Thank you
but what if I want to overload a non-defined type?
You need to provide the declaration of
void do_something(some_type const &input);
before you call do_something with an object of type some_type. Otherwise, the template version will be used.
#include "do_something.h"
#include "some_type.h"
// This is all you need. You can implement the function here
// or any other place of your choice.
void do_something(some_type const &input);
#include <boost/container/string.hpp>
int main()
{
do_something(std::string("whatever"));
do_something(boost::container::string("whatever"));
//oops, some_type() never defined in the header file, this
//function will call the template version, but this is not
//the behavior user expected
do_something(some_type());
}
I am working with the libMesh FEM library and am trying to develop a class (EqCore) that inherits from libMesh. This class will provide some additional features that are inherited again by a class that I want to actually use (MainEq).
The two functions, set_constant and get_constant, are causing the error below. These worked as shown with a different inheritance scheme (see Inheritance of template class with a template member function in C++). The difference with this problem is that now the template parameter (Type) is actually a class that gets inherited. Is this a dangerous practice?
I would appreciate any help getting this code working or finding an alternate method.
ERROR MESSAGES:
In member function ‘void EqCore::set_constant(std::string, ParamType)’:
test_libmesh.cpp:26:57: error: expected primary-expression before ‘>’ token
In member function ‘ParamType EqCore::get_constant(std::string)’:
/home/slaughter/Documents/programs/source/test_libmesh.cpp:31:76: error: expected primary-expression before ‘>’ token
PROGRAM:
//! \example test_libmesh.cpp
#include <string>
using std::string;
// libMesh includes
#include <libmesh.h>
#include <libmesh_common.h>
#include <equation_systems.h>
#include <transient_system.h>
#include <explicit_system.h>
#include <parameters.h>
#include <mesh.h>
using namespace libMesh;
// Fundamental behavior that will be used among many classes
template <typename Type> class EqCore : Type{
public:
// Class constructor
EqCore(EquationSystems& sys, string name) : Type(sys, name, 1){}
// A function for storing a constant value (causes error)
template<typename ParamType> void set_constant(std::string name, ParamType var){
Type::get_equation_systems().parameters.set<ParamType>(name) = var;
}
// A function for retrieving a constant value (causes error)
template<typename ParamType> ParamType get_constant(std::string name){
ParamType output = Type::get_equation_systems().parameters.get<ParamType>(name);
return output;
}
};
// A test class derived
class MainEq : public EqCore<ExplicitSystem>{
public:
// Constructor
MainEq(EquationSystems& sys) : EqCore(sys, "main"){ }
};
// Begin main function
int main (int argc, char** argv){
// Initialize libMesh and create an empty mesh
LibMeshInit init (argc, argv);
Mesh mesh;
// Test w/o any of the above classes
EquationSystems eq_sys(mesh);
eq_sys.parameters.set<double>("test1") = 1;
printf("Test 1: %f\n", eq_sys.parameters.get<double>("test1"));
// Test my class set/get functions
MainEq eq(eq_sys);
eq.set_constant<double>("test2", 2);
printf("Test 2: %f\n", eq.get_constant<double>("test2"));
}
Because you are inside a template, the compiler cannot determine that set is a template automatically during parse time, and it's assuming that set is a non-template, and hence the parse fails.
The solution is to explicitly inform the compiler that set is a member template, as such.
Type::get_equation_systems().parameters.template set<ParamType>(name) = var
In C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond, by David Abrahams, Aleksey Gurtovoy (Amazon) it is explained as follows:
double const pi = 3.14159265359;
template <class T>
int f(T& x)
{
return x.convert<3>(pi);
}
T::convert might be a member function template, in which case the
highlighted code passes pi to a specialization of convert<3>. It
could also turn out to be a data member, in which case f returns
(x.convert < 3 ) > pi. That isn't a very useful calculation, but the
compiler doesn't know it.
The template keyword tells the compiler that a dependent name is a
member template:
template <class T>
int f(T& x)
{
return x.template convert<3>(pi);
}
If we omit template, the compiler assumes that x.convert does not
name a template, and the < that follows it is parsed as the less-than
operator.