I've been thinking of a solution to validate the set of parameters a function/method receives using an object oriented approach. For example, in the following snippet the parameters are checked "manually" before being used.
InstallData::InstallData(std::string appPath, std::string appName,
std::string errMsg) {
if(appPath.empty()) {
#ifndef NDEBUG
std::cout << "Path not specified" << std::endl;
#endif
}
if(appName.empty()) {
#ifndef NDEBUG
std::cout << "Application name not specified" << std::endl;
std::cout << "Defaulting to AppName" << std::endl;
this->appName = "AppName";
#endif
}
if(errMsg.empty()) {
#ifndef NDEBUG
std::cout << "Error message not specified" << std::endl;
std::cout << "Defaulting to Error" << std::endl;
this->errMsg = "Error";
#endif
}
// ... further initialization beyond this point ...
}
As the number of parameters increases so does the size of the validation code. I've thought of a basic approach of checking parameters(strings and pointers) as whether they are either empty or null(the aim is to make the code providing functionality more readable).
class Validator {
public:
bool validateStrs(std::vector<std::string> strings, std::vector<std::string> messages, bool quiet);
bool validateStr(std::string str, std::string message, bool quiet);
bool validatePtrs(std::vector<void*> ptrs, std::vector<std::string> messages, bool quiet);
bool validatePtr(void* ptr, std::string message, bool quiet);
};
The validation methods validateStrs and validatePtrs check whether each element of the first array is empty or null and display a message from the second array(there is a one to one relationship between the elements of the first array and the second) if the quiet flag is not set.
In my implementation this looks like:
InstallData::InstallData(std::string appPath, std::string appName,
std::string errMsg, std::string errTitle) {
// Initialize string container
std::vector<std::string> strings;
strings.push_back(appPath);
strings.push_back(appName);
strings.push_back(errMsg);
strings.push_back(errTitle);
// Initialize name container
std::vector<std::string> names;
names.push_back("ApplicationPath");
names.push_back("ApplicationName");
names.push_back("ErrorMessage");
names.push_back("ErrorTitle");
boost::shared_ptr<Validator> valid(new Validator());
bool result = true;
#ifndef NDEBUG
result = valid->validateStrs(strings, names, false);
#else
result = valid->validateStrs(strings, names, true);
#endif
if(result){
this->appPath = appPath;
this->appName = appName;
this->errMsg = errMsg;
this->errTitle = errTitle;
} else {
std::exit(0);
}
}
The messages can also be placed in a separate file thus making the method body cleaner.
Numeric value range checkers can also be implemented similarly. This approach, however, doesn't consider dependencies between parameters.
Is there a more elegant solution of implementing a parameter validation mechanism, possibly using templates?
A more elegant way is not to use standard types for parameters but to define specific classes that check parameters on construction. Something like
class InvalidAppPath {};
class AppPath {
public:
AppPath(const std::string & appPath) : path(appPath) {
if ( appPath.empty() ) throw InvalidAppPath();
}
operator std::string() { return path; }
private:
std::string path;
};
This would also make it easier to ensure that an AppPath is checked for validity only on construction and possibly on modification.
These slides from a presentation by Ric Parkin at the 2007 ACCU Conference explore the idea in greater detail.
Perhaps you would find it easier to leverage function name overloading and variadic templates. You can group the parameter information you want to validate along with the corrective action together in a std::tuple. I implemented a small demo of this idea on IDEONE.
bool validate (std::string s) { return !s.empty(); }
bool validate (const void *p) { return p; }
template <typename Tuple>
bool validate (Tuple param) {
if (validate(std::get<0>(param))) return true;
#ifndef NDEBUG
std::cout << "Invalid: " << std::get<1>(param) << std::endl;
std::get<2>(param)();
#endif
return false;
}
bool validate () { return true; }
template <typename T, typename... Params>
bool validate (T param, Params... params) {
return validate(param) & validate(params...);
}
Then, you could use it like:
bool result
= validate(
std::make_tuple(appPath, "ApplicationPath",
[&](){ appPath = "defaultPath"; }),
std::make_tuple(appName, "ApplicationName",
[&](){ appName = "defaultName"; })
//...
);
Related
I need to make a class (we'll call it Command) that takes in a string, processes it into function arguments, and then passes it to a member function of a different class. For my use, the member function that I pass to Command could come from a number of classes, and could have many different prototypes. I can guarantee that that member function will return void. Here's the code I imagine:
class Command {
public:
vector<tuple<int, string, any>> argument_specification;
SomeType callable;
Command(vector<tuple<int, string, any>> argument_spec, SomeType callable) {
this->argument_specification = argument_spec;
this->callable = callable;
}
void apply(string args) {
/* processing args according to this->argument_specification
to make a std::tuple arguments */
std::apply(this->callable, arguments);
}
};
class Action {
public:
print_two_arguments(int arg1, int arg2) {
std::cout << arg1 << ", " << arg2 << std::endl;
}
print_one_arguments(std::string arg1) {
std::cout << arg1 << std::endl);
}
}
int main() {
Action *actor = new Action();
// my argument specification code splits by string and then extracts
// arguments by position or keyword and replacing with a default if
// not specified
Command *command1 = new Command({{0, "first_arg", "something"}},
&actor->print_one_argument);
command1->apply("hello_world"); // Should print "hello_world"
Command *command2 = new Command({{0, "first_arg", 2},
{1, "second_arg", 10}},
&actor->print_two_arguments);
command2->apply("0 2"); // should print "0 2"
}
I don't really mind what method gets there - I've tried std::bind and can't quite get that to work, I've also tried lambdas. I'm currently trying a template class with a type deduced factory method. I'm also open to a macro definition that will fix this at compile time.
A couple ideas come to mind, but the key thing that I'm seeing is that you want to be able to take an arbitrary void function and call it with a single string. Templates can be really helpful here because you can use them to auto-deduce things such as how to build the tuple that you apply to the function.
This will be a semi-complicated meta-program-y solution, but I love that stuff; so I'm going to build a prototype. Also beware, this is the kind of solution that will result in absolutely horrendous compiler errors if you try to use it wrong.
My suggestion would be to make Command a templated type, where the command itself is templated on the parameter types of the function you want to pass it. If you need to be able to make a list of these to apply arguments to, then you can have a base class which provides the apply function. Since I don't fully understand how the argument specification is supposed to work, I'm punting on that and supporting keyword arguments only; but the way I built this, it should be fairly straightfoward to sub in your own argument splitter. I think. It could be cleaner, but I need to get back to my job.
Play with it on Compiler Explorer: https://godbolt.org/z/qqrn9bs1T
#include <any>
#include <functional>
#include <initializer_list>
#include <iostream>
#include <iterator>
#include <memory>
#include <regex>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>
using namespace std;
// Converts the string arguments to the actual types
template <class T> T convert_arg(std::string);
template <> std::string convert_arg<std::string>(std::string s) { return s; }
template <> int convert_arg<int>(std::string s) { return std::stoi(s); }
// Split on spaces
std::vector<string> tokenize(std::string s) {
istringstream iss(s);
return {istream_iterator<string>{iss}, istream_iterator<string>{}};
}
// Argument spec defines how to parse the arguments from the input. It
// contains the positional index in the string, the name of it, and a
// default value. It's effectively a mapping from the string being applied
// to the function being called.
//
// This could maybe be turned into a std::tuple<std::tuple<...>>, but
// I'm not sure. That could get a little messy with trying to iterate
// through it to build the argument list, and I don't think it buys us
// anything.
//
// For example, given the argument spec
// {{1, "first_arg", 0}, {0, "second_arg", "some_default"}}
// You could call a function that has the signature
// void (int, string);
// And you could parse the following argument strings (assuming space-delimited)
// "second_arg=hello first_arg=0"
// "words 1"
// "first_arg=5 more_text"
using argument_spec_t = std::vector<tuple<std::size_t, string, std::string>>;
class CommandBase {
public:
virtual void apply(string args) = 0;
};
// Concrete commands are templated on the argument types of the function
// that they will invoke. For best results, use make_command() to deduce
// this template from the function that you want to pass the Command in
// order to get references and forwarding correct.
template <class... ArgTs> class Command : public CommandBase {
public:
using callable_t = std::function<void(ArgTs...)>;
// Holds the argument specification given during constuction; this
// indicates how to parse the string arguments
argument_spec_t m_argument_specification;
// A function which can be invoked
callable_t m_callable;
Command(argument_spec_t argument_spec, callable_t callable)
: m_argument_specification(std::move(argument_spec)),
m_callable(std::move(callable)) {}
void apply(string args) {
//std::cout << "Apply " << args << std::endl;
std::tuple parsed_args =
build_args(split_args(std::move(args), m_argument_specification),
std::index_sequence_for<ArgTs...>{});
std::apply(m_callable, parsed_args);
}
private:
// Pre-processes the command arguments string into a
// std::unordered_map<size_t, std::string> where x[i] returns the text of the
// i'th argument to be passed to the function.
//
// \todo Support positional arguments
// \todo Be more robust
static std::unordered_map<size_t, std::string>
split_args(std::string args, const argument_spec_t &arg_spec) {
std::unordered_map<std::string, std::string> kw_args;
std::unordered_map<size_t, std::string> arg_map;
vector<string> tokens = tokenize(args);
for (const auto &token : tokens) {
auto delim = token.find("=");
auto key = token.substr(0, delim);
auto val = token.substr(delim + 1);
kw_args[key] = val;
// std::cout << "key = " << val << std::endl;
}
for (size_t i = 0; i < arg_spec.size(); ++i) {
const auto &[pos_index, key, default_val] = arg_spec[i];
auto given_arg_it = kw_args.find(key);
if (given_arg_it != kw_args.end())
arg_map[i] = given_arg_it->second;
else
arg_map[i] = default_val;
// std::cout << i << " -> " << arg_map[i] << std::endl;
}
return arg_map;
}
// Copies the arguments from the map returned by pre_process_args into a
// std::tuple which can be used with std::apply to call the internal function.
// This uses a faux fold operation because I'm not sure the right way to do a
// fold in more modern C++
// https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html
template <std::size_t... Index>
std::tuple<ArgTs...>
build_args(std::unordered_map<size_t, std::string> arg_map,
std::index_sequence<Index...>) {
std::tuple<ArgTs...> args;
std::initializer_list<int> _{
(std::get<Index>(args) =
convert_arg<std::tuple_element_t<Index, std::tuple<ArgTs...>>>(
std::move(arg_map[Index])),
0)...};
return args;
}
};
// Factory function to make a command which calls a pointer-to-member
// function. It's important that the reference to the object stays in
// scope as long as the Command object returned!
template <class C, class... ArgTs>
std::unique_ptr<CommandBase> make_command(C &obj,
void (C::*member_function)(ArgTs...),
argument_spec_t argument_spec) {
return std::make_unique<Command<ArgTs...>>(
std::move(argument_spec), [&obj, member_function](ArgTs... args) {
(obj.*member_function)(std::forward<ArgTs>(args)...);
});
}
// Factory function to make a command which calls a std::function.
template <class... ArgTs>
std::unique_ptr<CommandBase>
make_command(std::function<void(ArgTs...)> callable,
argument_spec_t argument_spec) {
return std::make_unique<Command<ArgTs...>>(std::move(argument_spec),
std::move(callable));
}
// Factory function to make a command which calls a free function
template <class... ArgTs>
std::unique_ptr<CommandBase> make_command(void (*fn)(ArgTs...),
argument_spec_t argument_spec) {
return make_command(std::function<void(ArgTs...)>{fn},
std::move(argument_spec));
}
class Action {
public:
void print_two_arguments(int arg1, int arg2) {
std::cout << arg1 << ", " << arg2 << std::endl;
}
void print_one_argument(std::string arg1) { std::cout << arg1 << std::endl; }
};
void print_one_argument_free(std::string arg1) {
std::cout << arg1 << std::endl;
}
int main() {
Action actor;
// my argument specification code splits by string and then extracts
// arguments by position or keyword and replacing with a default if
// not specified
auto command1 = make_command(actor, &Action::print_one_argument,
argument_spec_t{{0, "first_arg", "something"}});
command1->apply("first_arg=hello_world"); // Should print "hello_world"
auto command2 = make_command(
actor, &Action::print_two_arguments,
argument_spec_t{{0, "first_arg", "2"}, {1, "second_arg", "10"}});
command2->apply("0 second_arg=2"); // should print "0 2"*/
auto command3 = make_command(&print_one_argument_free,
argument_spec_t{{0, "first_arg", "something"}});
command3->apply("first_arg=hello_again");
}
I think there are a number of ways to handle this problem, including function pointers with variable arguments, etc. But your fundamental problem is that you're asking one class to understand the internals of another class, which never works out well. I'd argue instead that you should have a parent Actor class that has a function that can be overridden by sub-classes and just passing an instance of the subclass instead. Each subclass may need to take an array of arguments, or even another container type that each subclass knows what it needs from within.
#include <iostream>
using namespace std;
class Data {
public:
std::string strdata;
int intinfo1;
int intinfo2;
};
class ActionBase {
public:
virtual void act(Data d) = 0;
};
class PrintIntinfos : public ActionBase {
public:
virtual void act(Data d) {
std::cout << d.intinfo1 << ", " << d.intinfo2 << std::endl;
}
};
class PrintStrData : public ActionBase {
public:
virtual void act(Data d) {
std::cout << d.strdata << std::endl;
}
};
int main()
{
ActionBase *Action1 = new PrintIntinfos();
Data d = Data();
d.intinfo1 = 42;
d.intinfo2 = -42;
Action1->act(d);
delete Action1;
d.strdata = "hello world";
Action1 = new PrintStrData();
Action1->act(d);
}
What you should actually do requires analysis of what your goals are with respect to base-pointers and containers and your data structure, flow, etc.
In your apply you describe something that really wants the context of the constructor. What if Command was
class Command {
std::function<void(std::string)> callable;
public:
template <typename... Args>
Command(std::function<std::tuple<Args...>(std::string)> argument_spec, std::function<void(Args...)> callable)
: callable([=](std::string args) { std::apply(callable, argument_spec(args)); })
{ }
void apply(std::string args) {
callable(args);
}
};
You would still be able to use your argument specification code to create the argument_spec parameter
I have a templated function inside a non-templated class, like so:
class Foo
{
public:
template <class T>
void func(T& val)
{
//do work here
}
}
Then, in main.cpp I'm doing:
Foo a;
std::string val;
a.func<std::string>(val); //this line gives the error
I get an error saying "primary expression expected before '>'". So I do a quick Google search and find that everyone suggests a simple solution:
a.template func<std::string>(val);
Only problem is, I'm still getting the exact same error.
EDIT :
The reason I wasn't giving the full example is because it involves external libraries and lengthy code that obscures the question, but since the simplified code above doesn't cut it. Here's the complete class that I wrote:
class ConfigFileReader
{
public:
ConfigFileReader() { }
ConfigFileReader(const std::string& config_file_path)
{
setConfigFilePath(config_file_path);
}
~ConfigFileReader() { }
void setConfigFilePath(const std::string& config_file_path)
{
try
{
root_node_ = YAML::LoadFile(config_file_path);
}
catch(const YAML::BadFile& file_load_exception)
{
printf("Error opening YAML file. Maybe the file path is incorrect\n%s", file_load_exception.msg.c_str());
}
}
template<class T>
bool getParam(const std::string& param_key, T& param_value)
{
if (root_node_.IsNull() || !root_node_.IsDefined())
{
printf("Root node is undefined or not set");
return false;
}
YAML::Node node = YAML::Clone(root_node_);
std::vector<std::string> split_name;
boost::split(split_name, param_key, boost::is_any_of("/"));
for(const std::string& str: split_name)
{
if (!node.IsMap())
{
std::cout << "Parameter was not found (Node is null)." << str << std::endl; //replace with printf
return false;
}
node = node[str];
}
if (node.IsNull() || !node.IsDefined())
{
std::cout << "Parameter was not found (Node is null/undefined)." << std::endl;
return false;
}
try
{
param_value = node.as<T>();
return true;
}
catch (const YAML::TypedBadConversion<T>& type_conversion_exception)
{
std::cout << "Error converting param value into specified data type" << std::endl;
std::cout << type_conversion_exception.msg << std::endl;
}
return false;
}
private:
YAML::Node root_node_;
};
Then, in a separate cpp file is the main function
int main(int argc, char** argv)
{
if (argc != 2)
{
printf("Incorrect number of arguments given");
return EXIT_FAILURE;
}
printf("Config file path: %s", argv[1]);
ConfigFileReader config_file_reader(std::string(argv[1]));
std::string param_out;
bool success = config_file_reader.template getParam<std::string>("controller/filepath", param_out); //<-- ERROR HERE
return EXIT_SUCCESS;
}
Compiler: gcc 4.8.4, and c++11 flag set when compiling.
EDIT 2:
Added string argument constructor to the code.
Your problem is that this:
ConfigFileReader config_file_reader(std::string(argv[1]));
Is interpreted as a forward declaration for a function named config_file_reader that accepts a pointer to a string. See the error message:
ConfigFileReader(std::__cxx11::string*) {aka ConfigFileReader(std::__cxx11::basic_string*)}'
This is because you've encountered The most vexing parse
Use ConfigFileReader config_file_reader(std::string{argv[1]}); to better disambiguate for the compiler that you are intending to construct an object. Then the compiler will start complaining that you're missing a constructor that accepts a string!
You do have a default constructor, so when we use that:
ConfigFileReader config_file_reader;
It works without issue.
So:
define a constructor for ConfigFileReader that accepts a string , then
Call it in such a way that the code is unambiguous
Demo
It's even simpler:
Foo a;
std::string val;
a.func(val); // The compiler assumes T = std::string
I have a ResourceManager which takes in classes of type Resource. Resource is a parent class of other classes such as ShaderProgram, Texture, Mesh and even Camera who are completely unrelated to one another.
Suffice it to say, the ResourceManager works. But there is one thing that is very tedious and annoying, and that's when I retrieve the objects from the ResourceManager. Here is the problem:
In order to get an object from ResourceManager you call either of these functions:
static Resource* get(int id);
static Resource* get(const std::string &name);
The first function checks one std::unordered_map by an integer id; whereas the second function checks another std::unordered_map by the name that is manually given by the client. I have two versions of these functions for flexibility sakes because there are times where we don't care what the object contained within ResourceManager is (like Mesh) and there are times where we do care about what it is (like Camera or ShaderProgram) because we may want to retrieve the said objects by name rather than id.
Either way, both functions return a pointer to a Resource. When you call the function, it's as easy as something like:
rm::get("skyboxShader");
Where rm is just a typedef of ResourceManager since the class is static (all members/functions are static). The problem though is that the rm::get(..) function returns a Resource*, and not the child class that was added to the ResourceManager to begin with. So, in order to solve this problem I have to do a manual type conversion so that I can get ShaderProgram* instead of Resource*. I do it like this:
auto s = static_cast<ShaderProgram*>(rm::get(name));
So, everytime I want to access a Resource I have to insert the type I want to actually get into the static_cast. This is problematic insofar that everytime someone needs to access a Resource they have to type convert it. So, naturally I created a function, and being that ShaderProgram is the subject here, thus:
ShaderProgram* Renderer::program(const std::string &name)
{
auto s = static_cast<ShaderProgram*>(rm::get(name));
return s;
}
This function is static, and ResourceManager is a static class so the two go well hand-in-hand. This is a nice helper function and it works effectively and my program renders the result just fine. The problem is what I have to do when I'm dealing with other Resources; that means for every Resource that exists, there has to be a type-conversion function to accommodate it. Now THAT is annoying. Isn't there a way I can write a generic type-conversion function something like this?
auto Renderer::getResource(classTypeYouWant T, const std::string &name)
{
auto s = static_cast<T*>(rm::get(name));
return s;
}
Here, the auto keyword causes the function to derive which type it's supposed to be dealing with and return the result accordingly. My first guess is that I might have to use templates; but the problem with templates is that I can't limit which types get inserted into the function, and I really REALLY don't want floating-point id numbers, char ids, let alone custom-defined ids. It's either string (might change to const char* tbh) or ints or else.
How can I create a generic conversion function like the one described above?
Have you looked at using dynamic_cast? If the conversion fails with dynamic_cast the the pointer will be set to nullptr. So you could either write overloads for each type or you could write a template function where you pass the the type you want to convert to as well as the string or id and if the conversion succeeds or fails return true or false.
template<typename T>
bool Renderer::getResource(T*& type, const std::string &name)
{
type = dynamic_cast<decltype(std::remove_reference<decltype(T)>::type)>(rm::get(name));
if (type == nullptr)
return false;
return true;
}
OK, I did not like the idea of a typeless storage, but maybe you find that basic program as a start point. There are a lot of things which must be beautified, but some work must remain :-)
Again: It is a design failure to solve something in that way!
In addition to your example code this solution provides a minimum of safety while checking for the stored type while recall the element. But this solution needs rtti an this is not available on all platforms.
#include <map>
#include <iostream>
#include <typeinfo>
class ResourcePointerStorage
{
private:
std::map< const std::string, std::pair<void*, const std::type_info*>> storage;
public:
bool Get(const std::string& id, std::pair<void*, const std::type_info*>& ptr )
{
auto it= storage.find( id );
if ( it==storage.end() ) return false;
ptr= it->second;
return true;
}
bool Put( const std::string& id, void* ptr, const std::type_info* ti)
{
storage[id]=make_pair(ptr, ti);
}
};
template < typename T>
bool Get(ResourcePointerStorage& rm, const std::string& id, T** ptr)
{
std::pair<void*, const std::type_info*> p;
if ( rm.Get( id,p ))
{
if ( *p.second != typeid(T)) { return false; }
*ptr= static_cast<T*>(p.first);
return true;
}
else
{
return 0;
}
}
template < typename T>
void Put( ResourcePointerStorage& rm, const std::string& id, T *ptr)
{
rm.Put( id, ptr, &typeid(T) );
}
class Car
{
private:
int i;
public:
Car(int _i):i(_i){}
void Print() { std::cout << "A car " << i << std::endl; }
};
class Animal
{
private:
double d;
public:
Animal( double _d):d(_d) {}
void Show() { std::cout << "An animal " << d << std::endl; }
};
int main()
{
ResourcePointerStorage store;
Put( store, "A1", new Animal(1.1) );
Put( store, "A2", new Animal(2.2) );
Put( store, "C1", new Car(3) );
Animal *an;
Car *car;
if ( Get(store, "A1", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "A2", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "C1", &car)) { car->Print(); } else { std::cout << "Error" << std::endl; }
// not stored object
if ( Get(store, "XX", &an)) { } else { std::cout << "Expected false condition" << std::endl; }
// false type
if ( Get(store, "A1", &car)) { } else { std::cout << "Expected false condition" << std::endl; }
};
I've found the solution to my question. I created a macro:
#define convert(type, func) dynamic_cast<type>(func)
Extremely generic and code-neutral which allows types to be dynamic_casted from the return type of the function. It also allows for doing checks:
if (!convert(ShaderProgram*, rm::get("skyboxShader")))
cerr << "Conversion unsuccessful!" << endl;
else cout << "Conversion successful!" << endl;
I hope my solution will help people who search for questions similar of this kind. Thanks all!
I have encounter a problem in my project on enums.
In EventDef.h,
enum EventDef {
EVT1 = 0,
EVT2,
EVT3,
EVT_NUM,
}
In this way, I can extend the EventDef system in another header UIEventDef.h by
#include "EventDef.h"
enum UIEventDef {
UIEVT1 = EVT_NUM,
UIEVT2,
UIEVT3,
}
But, there is a limitation that i can not do this in NetEvent.h the same way.
#include "EventDef.h"
enum NetEventDef {
NETEVT1 = EVT_NUM,
NETEVT2, //wrong: this will have the same value as UIEVT2
NETEVT3,
}
Is there a better compile time solution in C++ such as templates that can help ?
The idea of extensible enums is not inherently "bad design". In other languages there is a history of them, even if c++ does not support them directly. There are different kinds of extensibility.
Things that extensible enums would be useful for
error codes
message types
device identification (OIDs are a hierarchical enum like system)
Examples of enum extensibility
Objective Modula Two has enums that are extensible with a class like inheritance.
The Extensible Enum Pattern in Java, which can be implemented in c++.
Java enums are extensible in that extra data and methods can be a part of an enum.
In c++, the typeid operator is essentially a compiler generated enum with attached values.
The kind of extensibility you showed in your sample code does not have an elegant implementation in unaided c++. In fact, as you pointed out, it easily leads to problems.
Think about how you are wanting to use an extensible enum. Perhaps a set/map of immutable singleton objects will meet your needs.
Another way to have extensible enums in c++ is to use a code generator. Every compilation unit that wants to add to an extensible enum, records the ids in its own, separate, .enum file. At build time, before compilation, a script (ie perl, bash, ...) looks for all .enum files, reads them, assigns numeric values to each id, and writes out a header file, which is included like any other.
Why do you want your event enums to be declared like that? What do you gain by having them 'linked' if you will, they way you describe?
I would make them completely independent enums. Secondly, I recommend you not use the old style enums anymore. c++11 is here and available in gcc. You should use enum classes:
enum class EventDef : unsigned { Evt1 = 0, Evt2, Evt3, ... LastEvt }
enum class NetEvtDef : unsigned { NetEvt1 = 0, NetEvt2, NetEvt3, ... NetLastEvt }
If you are switching you can do this:
void doSwitch(EventDef evt_def)
{
switch(evt_def)
{
case EventDef::Evt1
{
// Do something;
break;
}
default:
// Do something;
};
}
void doSwitch(NetEvtDef net_def)
{
switch(net_def)
{
case NetEvtDef::NetEvt1
{
// Do something;
break;
}
default:
// Do something;
};
}
By creating an overloaded function for doSwitch you segregate all your enum types. Having them in separate categories is a benefit not a problem. It provides you the flexibility to deal with each event enum type differently.
Chaining them together as you describe needlessly complicates the problem.
I hope that helps.
I find the following a useful compromise between complexity, features, and type safety. It uses global variables of a custom class that has a default constructor to make initialisation easy. The example below is an extendable set of error codes. You might want to enclose within a name space also (but I typically don't bother).
//
// ErrorCodes.h
// ExtendableEnum
//
// Created by Howard Lovatt on 10/01/2014.
//
#ifndef ErrorCodes_h
#define ErrorCodes_h
#include <string>
class ErrorCodes {
public:
static int nextValue_;
explicit ErrorCodes(std::string const name) : value_{nextValue_++}, name_{name} {}
ErrorCodes() : ErrorCodes(std::to_string(nextValue_)) {}
int value() const { return value_; }
std::string name() const { return name_; }
private:
int const value_;
std::string const name_;
ErrorCodes(const ErrorCodes &);
void operator=(const ErrorCodes &);
};
int ErrorCodes::nextValue_ = 0; // Weird syntax, does not declare a variable but rather initialises an existing one!
ErrorCodes first;
ErrorCodes second;
// ...
#endif
//
// ExtraErrorCodes.h
// ExtendableEnum
//
// Created by Howard Lovatt on 10/01/2014.
//
#ifndef ExtraErrorCodes_h
#define ExtraErrorCodes_h
#include "ErrorCodes.h"
ErrorCodes extra{"Extra"};
#endif
//
// ExtraExtraExtraCodes.h
// ExtendableEnum
//
// Created by Howard Lovatt on 10/01/2014.
//
#ifndef ExtendableEnum_ExtraExtraCodes_h
#define ExtendableEnum_ExtraExtraCodes_h
#include "ErrorCodes.h"
ErrorCodes extraExtra{"ExtraExtra"};
#endif
//
// main.cpp
// ExtendableEnum
//
// Created by Howard Lovatt on 10/01/2014.
//
#include <iostream>
#include "ErrorCodes.h"
#include "ExtraErrorCodes.h"
#include "ExtraExtraErrorCodes.h"
// Need even more error codes
ErrorCodes const localExtra;
int main(int const notUsed, const char *const notUsed2[]) {
std::cout << first.name() << " = " << first.value() << std::endl;
std::cout << second.name() << " = " << second.value() << std::endl;
std::cout << extra.name() << " = " << extra.value() << std::endl;
std::cout << extraExtra.name() << " = " << extraExtra.value() << std::endl;
std::cout << localExtra.name() << " = " << localExtra.value() << std::endl;
return 0;
}
The output is:
0 = 0
1 = 1
Extra = 2
ExtraExtra = 3
4 = 4
If you have multiple compilation units then you need to use a variation on the singleton pattern:
class ECs {
public:
static ErrorCode & first() {
static ErrorCode instance;
return instance;
}
static ErrorCode & second() {
static ErrorCode instance;
return instance;
}
private:
ECs(ECs const&);
void operator=(ECs const&);
};
We can construct an extensible “enum” in C++ as follows:
struct Last {};
struct D
{
using Next = Last;
static const char* name = “D”;
};
struct C
{
using Next = D;
static const char* name = “C”;
};
struct B
{
using Next = C;
static const char* name = “B”;
};
using First = B;
We can iterate thru the above using these constructs:
void Process(const B&)
{
// do something specific for B
cout << “Call me Ishmael” << endl;
}
template <class T>
void Process(const T&)
{
// do something generic
cout << “Call me “ << T::name << endl;
}
template <class T>
struct IterateThru
{
static void iterate()
{
Process(T());
IterateThru<T::Next>::iterate();
}
};
template <>
struct IterateThru<Last>
{
static void iterate()
{
// end iteration
}
};
To iterate through the “enumeration”:
IterateThru<First>::iterate();
To extend the “enumeration”:
struct A
{
using Next = B;
static const char* name = “A”;
}:
using First = A:
Is there a __CLASS__ macro in C++ which gives the class name similar to __FUNCTION__ macro which gives the function name
The problem with using typeid(*this).name() is that there is no this pointer in a static method call. The macro __PRETTY_FUNCTION__ reports a class name in static functions as well as method calls. However, this will only work with gcc.
Here's an example of extracting the information through a macro style interface.
inline std::string methodName(const std::string& prettyFunction)
{
size_t colons = prettyFunction.find("::");
size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
size_t end = prettyFunction.rfind("(") - begin;
return prettyFunction.substr(begin,end) + "()";
}
#define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__)
The macro __METHOD_NAME__ will return a string of the form <class>::<method>(), trimming the return type, modifiers and arguments from what __PRETTY_FUNCTION__ gives you.
For something which extracts just the class name, some care must be taken to trap situations where there is no class:
inline std::string className(const std::string& prettyFunction)
{
size_t colons = prettyFunction.find("::");
if (colons == std::string::npos)
return "::";
size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
size_t end = colons - begin;
return prettyFunction.substr(begin,end);
}
#define __CLASS_NAME__ className(__PRETTY_FUNCTION__)
The closest thing there's is to call typeid(your_class).name() - but this produces compiler specific mangled name.
To use it inside class just typeid(*this).name()
Not yet. (I think __class__ is proposed somewhere). You can also try to extract class part from __PRETTY_FUNCTION__.
I would like to suggest boost::typeindex, which I learned about from Scott Meyer's "Effective Modern C++" Here's a basic example:
Example
#include <boost/type_index.hpp>
class foo_bar
{
int whatever;
};
namespace bti = boost::typeindex;
template <typename T>
void from_type(T t)
{
std::cout << "\tT = " << bti::type_id_with_cvr<T>().pretty_name() << "\n";
}
int main()
{
std::cout << "If you want to print a template type, that's easy.\n";
from_type(1.0);
std::cout << "To get it from an object instance, just use decltype:\n";
foo_bar fb;
std::cout << "\tfb's type is : "
<< bti::type_id_with_cvr<decltype(fb)>().pretty_name() << "\n";
}
Compiled with "g++ --std=c++14" this produces the following
Output
If you want to print a template type, that's easy.
T = double
To get it from an object instance, just use decltype:
fb's type is : foo_bar
I think using __PRETTY_FUNCTION__ is good enough though it includes namespace as well i.e. namespace::classname::functionname until __CLASS__ is available.
If you need something that will actually produce the class name at compile time, you can use C++11 to do this:
#define __CLASS__ std::remove_reference<decltype(classMacroImpl(this))>::type
template<class T> T& classMacroImpl(const T* t);
I recognize that this is not the same thing as __FUNCTION__ but I found this post while looking for an answer like this. :D
I created a function using __PRETTY_FUNCTION__ and constexpr with C++17 constexpr std::string_view methods. I also updated the algorithm a bit to be more reliably (Thanks to #n. 'pronouns' m for your help in 64387023).
constexpr std::string_view method_name(const char* s)
{
std::string_view prettyFunction(s);
size_t bracket = prettyFunction.rfind("(");
size_t space = prettyFunction.rfind(" ", bracket) + 1;
return prettyFunction.substr(space, bracket-space);
}
#define __METHOD_NAME__ method_name(__PRETTY_FUNCTION__)
In C++20, one can declare the function as consteval forcing it to evaluate at compile-time. Furthermore, there is std::basic_fixed_string for use as template parameter.
If your compiler happens to be g++ and you are asking for __CLASS__ because you want a way to get the current method name including the class, __PRETTY_FUNCTION__ should help (according to info gcc, section 5.43 Function Names as Strings).
If you're talking MS C++ (You should state, esp as __FUNCTION__ is a non-standard extension), there are __FUNCDNAME__ and __FUNCSIG__ symbols which you could parse
You can get the function name including class name.
This can process C-type funcitons.
static std::string methodName(const std::string& prettyFunction)
{
size_t begin,end;
end = prettyFunction.find("(");
begin = prettyFunction.substr(0,end).rfind(" ") + 1;
end -= begin;
return prettyFunction.substr(begin,end) + "()";
}
My solution:
std::string getClassName(const char* fullFuncName)
{
std::string fullFuncNameStr(fullFuncName);
size_t pos = fullFuncNameStr.find_last_of("::");
if (pos == std::string::npos)
{
return "";
}
return fullFuncNameStr.substr(0, pos-1);
}
#define __CLASS__ getClassName(__FUNCTION__)
I works for Visual C++ 12.
Here's a solution based on the __FUNCTION__ macro and C++ templates:
template <class T>
class ClassName
{
public:
static std::string Get()
{
// Get function name, which is "ClassName<class T>::Get"
// The template parameter 'T' is the class name we're looking for
std::string name = __FUNCTION__;
// Remove "ClassName<class " ("<class " is 7 characters long)
size_t pos = name.find_first_of('<');
if (pos != std::string::npos)
name = name.substr(pos + 7);
// Remove ">::Get"
pos = name.find_last_of('>');
if (pos != std::string::npos)
name = name.substr(0, pos);
return name;
}
};
template <class T>
std::string GetClassName(const T* _this = NULL)
{
return ClassName<T>::Get();
}
Here's an example of how this could be used for a logger class
template <class T>
class Logger
{
public:
void Log(int value)
{
std::cout << GetClassName<T>() << ": " << value << std::endl;
std::cout << GetClassName(this) << ": " << value << std::endl;
}
};
class Example : protected Logger<Example>
{
public:
void Run()
{
Log(0);
}
}
The output of Example::Run will then be
Example: 0
Logger<Example>: 0
This works quite nicely if you are willing to pay the cost of a pointer.
class State
{
public:
State( const char* const stateName ) :mStateName( stateName ) {};
const char* const GetName( void ) { return mStateName; }
private:
const char * const mStateName;
};
class ClientStateConnected
: public State
{
public:
ClientStateConnected( void ) : State( __FUNCTION__ ) {};
};
Works with msvc and gcc too
#ifdef _MSC_VER
#define __class_func__ __FUNCTION__
#endif
#ifdef __GNUG__
#include <cxxabi.h>
#include <execinfo.h>
char *class_func(const char *c, const char *f)
{
int status;
static char buff[100];
char *demangled = abi::__cxa_demangle(c, NULL, NULL, &status);
snprintf(buff, sizeof(buff), "%s::%s", demangled, f);
free(demangled);
return buff;
}
#define __class_func__ class_func(typeid(*this).name(), __func__)
#endif
All the solutions posted above that rely on the __PRETTY_FUNCTION__ do have specific edge case(s) where they do not return the class name / class name only. For example, consider the following pretty function value:
static std::string PrettyFunctionHelper::Test::testMacro(std::string)
Using the last occurence of "::" as delimter won't work since the function parameter also contains a "::" (std::string).
You can find similar edge cases for "(" as delimiter and more.
The only solution I found takes both the __FUNCTION__ and __PRETTY_FUNCTION__ macros as parameters. Here is the full code:
namespace PrettyFunctionHelper{
static constexpr const auto UNKNOWN_CLASS_NAME="UnknownClassName";
/**
* #param prettyFunction as obtained by the macro __PRETTY_FUNCTION__
* #return a string containing the class name at the end, optionally prefixed by the namespace(s).
* Example return values: "MyNamespace1::MyNamespace2::MyClassName","MyNamespace1::MyClassName" "MyClassName"
*/
static std::string namespaceAndClassName(const std::string& function,const std::string& prettyFunction){
//AndroidLogger(ANDROID_LOG_DEBUG,"NoT")<<prettyFunction;
// Here I assume that the 'function name' does not appear multiple times. The opposite is highly unlikely
const size_t len1=prettyFunction.find(function);
if(len1 == std::string::npos)return UNKNOWN_CLASS_NAME;
// The substring of len-2 contains the function return type and the "namespaceAndClass" area
const std::string returnTypeAndNamespaceAndClassName=prettyFunction.substr(0,len1-2);
// find the last empty space in the substring. The values until the first empty space are the function return type
// for example "void ","std::optional<std::string> ", "static std::string "
// See how the 3rd example return type also contains a " ".
// However, it is guaranteed that the area NamespaceAndClassName does not contain an empty space
const size_t begin1 = returnTypeAndNamespaceAndClassName.rfind(" ");
if(begin1 == std::string::npos)return UNKNOWN_CLASS_NAME;
const std::string namespaceAndClassName=returnTypeAndNamespaceAndClassName.substr(begin1+1);
return namespaceAndClassName;
}
/**
* #param namespaceAndClassName value obtained by namespaceAndClassName()
* #return the class name only (without namespace prefix if existing)
*/
static std::string className(const std::string& namespaceAndClassName){
const size_t end=namespaceAndClassName.rfind("::");
if(end!=std::string::npos){
return namespaceAndClassName.substr(end+2);
}
return namespaceAndClassName;
}
class Test{
public:
static std::string testMacro(std::string exampleParam=""){
const auto namespaceAndClassName=PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__);
//AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<namespaceAndClassName;
assert(namespaceAndClassName.compare("PrettyFunctionHelper::Test") == 0);
const auto className=PrettyFunctionHelper::className(namespaceAndClassName);
//AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<className;
assert(className.compare("Test") == 0);
return "";
}
};
}
#ifndef __CLASS_NAME__
#define __CLASS_NAME__ PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__)
#endif
Following method (based on methodName() above) can also handle input like "int main(int argc, char** argv)":
string getMethodName(const string& prettyFunction)
{
size_t end = prettyFunction.find("(") - 1;
size_t begin = prettyFunction.substr(0, end).rfind(" ") + 1;
return prettyFunction.substr(begin, end - begin + 1) + "()";
}