I'm writing a compiler in C++ and as any compiler, it requires an extreme amount of pattern matching and dynamic casts. In languages like Rust, Haskell, and OCaml I can destruct a type easily, like:
match node {
Binary{ left, right, operator } => { .. }
_ => { .. }
}
In C++ the best I can do is:
if (auto bin = dynamic_cast<Binary*>(node)) { ... }
else if (...) { ... }
Which is really limited and ugly if you introduce smart pointers into the scene. For example if I need to match 2 things for something:
bool matched = false;
if (auto m1 = dynamic_cast<Foo*>(a)) {
if (auto m2 = dynamic_cast<Bar*>(b)) {
matched = true;
}
}
if (!matched) {
// This is because C++ does not allow you to declare two variables inside the condition...
}
I know about the Mach7 library but to be honest it seems awful as you need to write metadata for your structures (also as I've noticed it has quite a lot of bugs and limitations).
Is there a way to make these kind of matches more readable?
The following seems to be a way to avoid the double if for two matches - and can easily be generalized:
template <class T1,class T2> struct castPairstruct : public std::pair<T1,T2> {
operator bool() {return first && second;}
castPairstruct<T1,T2>(T1 a,T2 b):std::pair<T1,T2>(a,b) {;}
};
template <class T1,class T2> castPairstruct<T1,T2> castPair(T1 a,T2 b){
return castPairstruct<T1,T2>(a,b);
}
if (auto j=castPair(dynamic_cast<Foo*>(a),dynamic_cast<Bar*>(b)) {
Related
I have a function that takes a T and calls specific functions on the supplied object. Until now it was used from compile-time objects, so all was great. Minimal example:
#include <iostream>
struct A {
void fun() const { std::cout << "A" << std::endl; }
};
struct B {
void fun() const { std::cout << "B" << std::endl; }
};
template<class T>
void use_function(const T& param) {
param.fun();
}
int main() {
use_function(A{}); // "A"
use_function(B{}); // "B"
return 0;
}
Now I'm trying to use that use_function() with objects that get created at runtime and having a hard time. I can't use std::variant or std::any since I need to supply the type as template parameter for their access functions - although all their variants fulfil the function interface. Example for a (failing) variant approach:
using var_type = std::variant<A, B>;
struct IdentityVisitor {
template<class T>
auto operator()(const T& alternative) const -> T {
return alternative;
}
};
int main() {
var_type var = A{};
// error C2338: visit() requires the result of all potential invocations to have the same type and value category (N4828 [variant.visit]/2).
use_function(std::visit(IdentityVisitor{}, var));
return 0;
}
What is possible is directly calling the function with an appropriate type like this:
if (rand() % 2 == 0)
use_function(A{});
else
use_function(B{});
just storing it in between is what I can't get working.
I understand on a technical level but having trouble coming up with an elegant solution. Is there one? I know that I could rewrite the objects with even a lightweight inheritance - but was trying to see if it's feasible to avoid it altogether, even if just as an exercise to avoid OOP in favor of templates and concepts. I feel like variants should be working with this, but apparently not.
std::visit([](auto const& x) { use_function(x); }, var);
If overload sets were objects, you could pass use_function to std::visit directly. Because they aren't, you need to wrap it in something that will be instantiated as a call to the right overload.
std::visit([](auto const& x) { use_function(x); }, var);
I have some code that looks like this:
for (const auto& query_result_row : query_result) {
my_struct.a = query_results_row["a"].as<int>();
my_struct.b = query_results_row["b"].as<string>();
// and so forth.
}
In general, there might be quite a number of fields of different types. And that's good, but if a selected field is NULL, this will throw an exception. So instead I've now written this, which is highly awkward and more than a bit error-prone:
for (const auto& query_result_row : query_result) {
if (!query_results_row["a"].is_null()) {
my_struct.a = query_results_row["a"].as<int>();
}
if (!query_results_row["ab"].is_null()) {
my_struct.b = query_results_row["b"].as<string>();
}
// and so forth.
}
Ick.
Perhaps, I thought, I could make a (templated) function that simplifies this:
for (const auto& query_result_row : query_result) {
MaybeSet(my_struct.a, query_results_row["a"]);
MaybeSet(my_struct.b, query_results_row["b"]);
// and so forth.
}
The problem here is that query_results_row["a"] is itself a templated type, and, moreover, the as() type may not be precisely the same as the my_struct type (e.g., different types of ints) and although I don't see it today, that smells like the sort of thing that some day could lead to an unintended cast.
// Doesn't work.
template <typename valueT, typename postgresFieldT>
void MaybeSet(valueT& my_struct_field, const postgresFieldT& field) {
if (!field.is_null()) {
my_struct_field = field.as<valueT>();
}
}
Any suggestions on a cleaner way to express this idea of setting things if they're not not null but not trying if they are?
For future generations: I slightly changed the problem and so arrived at a simple solution.
template <typename T>
class SetThing {
// ...
void MaybeSet(const pqxx::result::field& field) {
if (!field.is_null()) {
SetClean(field.as<T>());
}
}
template <typename asT>
void MaybeSet(const pqxx::result::field& field) {
if (!field.is_null()) {
SetClean(field.as<asT>());
}
}
// ...
}
Usage then becomes
SetThing thing;
thing.MaybeSet(query_result_row["dog"]);
thing.MaybeSet<the_cast_type>(query_result_row["complicated"]);
The second form is used for a handful of complex types (e.g., classes) that I can construct from something simple (say, an int or a string) but which isn't the base (templated) type of the SetThing.
I understand how Polymorphism and Generics interact in other programming languages (Java, C#, Typescript, ect.). In C++ however it feels like a pattern I would like to utilize fails.
In this example I want to have a list of Names which extend Words. I want to pass my list of names into a method which accepts a list of words, but I cannot. I can populate a list of words with my names, this however loses the type information, meaning I cannot call any methods inherit to the Name class.
#include <iostream>
#include <string>
#include <list>
class Word{
public:
virtual void say() = 0;
};
class Name : public Word{
std::string name;
public:
Name(std::string name){
this-> name = name;
}
void say() override{
std::cout << name << std::endl;
}
void important_name_function(){
// Something very important I want to call
}
};
void say_one(Word* w){
w-> say();
}
void say_all(std::list<Word*> list){
for(Word* w: list){
w-> say();
}
}
int main(){
std::list<Word*> words = {new Name("Kai"), new Name("Ben"), new Name("Sam")};
say_one(words.front()); //Works, due to the magic of polymorphism
say_all(words); //Works, due to the magic of polymorphism
std::list<Name*> names = {new Name("Kai"), new Name("Ben"), new Name("Sam")};
say_one(names.front()); //STILL works due to the magic of polymorphism AND type information is retained
say_all(names); //Fails but feels like it shouldn't
}
In, for example, Java I would be able to solve this issue by defining say all as
static <T extends Word> void say_all (java.util.LinkedList<T> list){
for(T w:list){
w.say();
}
}
However, looking for this solution in C++ gets what to my eyes looks like an ugly solution (C++ equivalent of using <T extends Class> for a java parameter/return type)
To me this means that one of the following is true:
This pattern is inherently undesirable, and should not be pursued.
The solution I dismissed as ugly is in fact the best solution and/or
I am incorrectly accessing it as ugly There is another solution to
creating this pattern
I am incorrectly assessing it as ugly
That.
I don't find the following ugly:
template<class T>
void say_all(const std::list<T*>& list) {
for (T* w : list) {
w->say();
}
}
Note that you don't have to restrict T at all in your example. Can't really match that in Java.
Only if you actually need to restrict T to an instance of Word:
template<class T, typename = std::enable_if_t<std::is_base_of<Word, T>::value>>
void say_all(const std::list<T*>& list) {
for (T* w : list) {
w->say();
}
}
Or with concepts:
template<typename T>
concept IsWord = std::is_base_of<Word, T>::value;
template<class T> requires IsWord<T>
void say_all(const std::list<T*>& list) {
for(T* w : list) {
w->say();
}
}
Side notes:
avoid copying objects unnecessarily by passing them by reference.
to reduce memory leaks avoid operator new and use std::list<std::unique_ptr<Word>> and std::make_unique instead.
You should be able to implement a generic function similar to java one using ::std::is_base_of type trait:
template
<
typename x_Word
, typename x_Enabled = ::std::enable_if_t
<
::std::is_base_of_v<Word, x_Word>
>
>
auto
say_all(::std::list<x_Word *> & words) -> void
{
for(auto & p_w: words)
{
p_w->say();
}
return;
}
online compiler
You're not wrong -- this is something C++ isn't great at. It doesn't currently have an equivalent to Java's bounded type parameters, meaning that if you want that specific level of control over what say_all can take, rather than just doing template<typename T> void say_all(list<T> const& l) (or even template<typename T> void say_all(T const& l)) and counting on the internal usage to throw errors, you'll need to do that manually, with enable_if and friends.
This is something that the maybe-upcoming C++ "concepts" feature is intended to address:
template<typename T> requires DerivedFrom<T, Word> void say_all(list<T> const& l) { ...
(Note that syntax and standard library support is still subject to change).
Still, in this case that's just in service of a guaranteed, early, and easy-to-troubleshoot compiler error if you try to pass a list of something else in. Honestly, my approach here would probably be to just document that say_all expects a list of something subclassing Name, and rely on a probable compiler error if that gets violated.
When using C-style return codes to signal errors, it's pretty common to see code like this:
if (do_something()) {
do_something_else();
} else {
report_failure();
}
Sometimes, if one block is much larger than the other, you might want to reorder the "handle failure" block before the "do_something_else" block.
if (!do_something()) {
report_failure();
} else {
do_something_else();
}
(Or, when it really is C-code the codes may be such that 0 indicates success rather than failure, but let's ignore that.)
When I use C++ idioms like boost::optional or one of the proposed std::expected types, usually what I want to do is put a declaration inside the condition of the if statement:
if (auto ok = do_something()) {
do_something_else(*ok);
} else {
report_failure(ok.error());
}
I like to do this because this way, ok is strictly contained in scope, it's not visible outside the two blocks.
However, once I do it this way, I can't reorder the two branches if I want, which probably annoys me much more than it should, but still.
What I would really like is a syntax
if not (auto ok = do_something()) {
report_failure(ok.error());
} else {
do_something_else(*ok);
}
But to my knowledge that doesn't actually work.
Is there a trick to accomplish that?
C++17 will introduce this syntax:
if (auto ok = do_something(); !ok) {
report_failure(ok.error());
} else {
do_something_else(*ok);
}
Which is basically what you want.
It is in the feature-complete draft.
You can add an extra scope:
{
auto ok = do_something();
if (! ok) {
report_failure(ok.error());
} else {
do_something_else(*ok);
}
}
Personally I wouldn't add those braces as the scope should be clear from the rest of the code, if you have too much functionality in one function you should refactor the code anyways...
Alright, so here's a little class that does what you want. Dress it up however you like.
template <typename T>
struct not_optional_type: public optional <T>
{
typedef optional <T> base_type;
not_optional_type( const base_type& v ): base_type( v ) { }
operator bool () const { return !(base_type)(*this); }
T operator * () const { return *(base_type)(*this); }
};
template <typename T>
not_optional_type <T>
not_optional( const optional <T> && v )
{
return not_optional_type <T> ( v );
}
Use it as you would expect:
if (auto ok = not_optional( do_something() ))
fooey();
else
success( *ok );
I personally think the proposed if syntax modification is an abomination.
Well, a lot of dirty tricks come to mind involving macros, but, supposing you don't want to go there, here's a non-macro trick:
template <class T> class notter {
T d_t;
public:
notter(T &t) : d_t(t) {}
notter(T t) : d_t(t) {}
operator bool() { return !d_t; }
T &data() { return d_t; }
};
Now you can use it as:
if (notter<int> a = do_something()) {
report_failure();
}
else {
do_something_else(a.data());
}
This assumes that do_something returns an int. You may avoid naming the type with decltype like this:
if (notter<decltype(do_something())> a = do_something()) {
but in cases like this, that may be overkill.
You may tweak it to your needs, if, say, data() is too verbose for you, or you want just one of the constructors, or to make a more "drop-in replacement" for optional<> (as per comments from Duthomhas) or expected<> - you may employ template specialization.
Also, you can take hint from std::make_shared() and such:
template<class T> notter<T> make_notter(T t) { return notter<T>(t); }
and use it like:
if (auto a = make_notter(do_something())) {
Is it possible to write a single templated function to increment the (numeric) fields of different structs? For example:
struct Color
{
ubyte a,r,g,b;
}
struct Point
{
double x, y;
}
I tried something like this:
T update(T, A)(T t, A a)
if (is(T == struct))
{
auto vals = t.tupleof;
foreach (i; 0 .. vals.length) {
vals[i] += a; // error: i cannot be read at compile time
}
return T(vals); // convert back to struct
}
I have also tried writing function templates that accept tuples, but the tuples are always expanded, which prevents the compiler from matching the correct template.
Thanks.
Well, I'd say that what you're trying doing is rather bizarre, but it's certainly possible. The most naive, in-place way would probably be:
void update(T)(ref T t)
if(is(T == struct))
{
foreach(ref var; t.tupleof)
++var;
}
The simplest way to do it with a copy would probably be to copy it and then update it rather than trying to construct a new one with updated values (though I'm sure that that can be done too if you really want to):
T update(T)(T t)
if(is(T == struct))
{
auto copy = t;
foreach(ref var; copy.tupleof)
++var;
return copy;
}
The main problem here, of course, is that the template constraint on both of these is far too weak. All you have to do is have unincrementable types in your struct, and it won't work. The simplest way to fix that would probably be to create an eponymous template to test it for you:
T update(T)(T t)
if(isIncrementableStruct!T)
{
auto copy = t;
foreach(ref var; copy.tupleof)
++var;
return copy;
}
template isIncrementableStruct(T)
{
enum isIncrementableStruct = is(T == struct) &&
is(typeof({T t; foreach(var; t.tupleof) ++var;}));
}
And if you want to be able to increment all of the fields that are incrementable and leave the others alone, you'd probably do something like:
T update(T)(T t)
if(is(T == struct))
{
auto copy = t;
foreach(ref var; copy.tupleof)
{
static if(canIncrement!(typeof(var)))
++var;
}
return copy;
}
template canIncrement(T)
{
enum canIncrement = is(typeof({T var; ++var;}));
}
In any case, the main thing that you appear to have missed was to attempt iterating over tupleof directly while using ref so that the elements were updated rather than having copies of them being updated.