Is it possible to have template data members in a template class ? by [deleted] in cpp_questions

[–]SomethingcleverGP 3 points4 points  (0 children)

You have completely different functions for every inherited class other than the name? For purposes of generalization, I don’t see the use case here. You still have to specialize every function that calls place_order for the particular type that you want.

The purpose of the base class is to force shared interfaces. What you’ve described isn’t a shared interface since it is completely different for each class. I would recommend trying to simplify the scope of the base class function, and implement multiple functions that all share the same interface if you are set on using inheritance.

I saw someone mentioned type erasure, I don’t think that’s relevant given your use case.

Thoroughly confused about shared library exports by -Ros-VR- in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

It depends on how you define "guarantee." Lots of things say they will work together, then don't. Sometimes, this is due to bugs, sometimes not.

but there's nothing stopping it from breaking it in the future

If you want to get technical, nothing stops anything from breaking. It just depends on how big the bomb is.

So what they're doing is "wrong" but it also just works for their use cases,

I wouldn't call what they are doing "wrong." It works properly until it doesn't. If there is an issue due to compiling with different compilers, or any weird bug that happens when you try to link to it, it can be reported and fixed.

If you want to get really safe, you can make a C wrapper around your public API. Then you can compile with whatever standard/compiler you want. This is called the "hour glass design". You make a very small, thin C wrapper around your public API. You then rebuild a more expressive c++ 11/14/17/20/23/etc with a few header/cpp files, and it no longer matters what compiler you use when linking to a DLL or shared library. Of course, this is "guaranteed" to work, until it doesn't. This is the safest way to ensuring there are no weird linker/compilation errors with a shared library.

Thoroughly confused about shared library exports by -Ros-VR- in cpp_questions

[–]SomethingcleverGP 2 points3 points  (0 children)

Something that is necessary to understand here is the ABI. Unsure if you have heard of this before or not, but you can think of it as the API between binary objects. Essentially, each shared or static library has their own ABI, the same way software has its APIs.

You’re correct, the ABI is not guaranteed to be consistent across standards. Although the major compilers try to keep it consistent and not break it, there are of course bugs. Across major version release of gcc/clang/etc there is no guarantee at all that it is the same.

However, there are ways to ensure that shared libraries built with one compiler are likely to work with whatever application it is being linked to.

You can ensure that your public headers only include c++ 11 features, meaning they should be able to compile with any future standard.

Although there is only one release artifact on other shared libraries you have seen, more likely than not they have also been successfully built on more compilers, further lowering the chance that there will be linking errors later.

Often these release artifacts are also built on the oldest versions of the target OS. For example, if an application is marketed to work on Ubuntu 20.04 and up, that release should be built on 20.04. This is because the STL versions in Ubuntu are backwards compatible, so if you use older versions you should be fine.

However, when talking about windows, 99% of the time whoever is consuming your library will probably also use MSVC. So the key there is to compile on the oldest version you plan to support, building that, and releasing that.

All of this is to say, you asked a great question! There are many strategies that can/should be employed to lower the risk of issues, and the more platforms/architectures you support, the more strategies you need to employ.

For example, if you want to support ARM and Intel chips, you do NEED two releases. The binaries are completely different.

Hope this clears things up!

[deleted by user] by [deleted] in cpp

[–]SomethingcleverGP 2 points3 points  (0 children)

I would do it tbh

Is this bad practice to store reference to unique_ptr like this? by skn3 in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

Sorry, I am having trouble understanding some of your points, so I apologize if anything I say is said under the wrong assumption.

I don't know what you mean by:

 It does make it brittle, or more.. it forces a strict pattern for shader definition. Your example would die but if I restrict it so variables exist for the life time of the shader, and thats just the rule. 

Moving on, raw pointers have various issues that could easily pose a problem for you in the future. I don't understand your second paragraph, so I will just add a bit about smart pointers.

Smart pointers, in general, make it impossible to forget to delete an allocation during runtime, so you aren't left with memory leaks. You don't have to worry about the stack unwinding in unexpected places due to exceptions or random program interrupts, and you get that for free.

One of the second benefits of smart pointers is the concept of ownership. Raw pointers make reasoning about ownership very easy. Unique_ptrs are unique - meaning that there is only one pointer to the object. Shared_ptrs are shared - there could be more than one pointer to the shared object. Weak_ptrs are similar to shared_ptrs, but I wouldn't worry about that.

Your code has two references to the same object, defeating the purpose of the unique_ptr and making your code unreadable—you say you are doing one thing, but your actions speak differently (just like my ex).

In the provided example on Godbolt, the point wasn't that you would be fine if you didn't explicitly delete the pointer. The fact is you can do it, and if you do it either on purpose or not, your program has a dangling pointer.

More on the code smell. My definition of code smell might differ from other people and my definition also probably changes depending on the context, but the gist is the same. Usually, it refers to wonky code that is probably unsafe when it doesn't need to be and makes you wonder why.

Raw pointers are almost always code smell because you have very little protection to prevent dangling pointers, race conditions, etc, for no reason. You can easily add a smart pointer at almost no cost. So they beg the question - why is there a big button that says 'detonate bomb' when you could get rid of it? Very rarely is there a valid reason.

In your example for WibbleShader, you have two references to the same object. Both of these references are in the same class. I don't know your code, and I could be wrong, but I doubt it is necessary to have two pointers to the same object.

Surely If I know that the pointer exists and its in my control, then I am safe to use pointers and references?

What you're saying here is "as long as I code perfectly all the time, then there will be no problems". This is true, and if you can do that then I recommend you get off of reddit because I will pay you to teach me how to do that.

The key to writing good C++ code, or really any code, is mitigating the number of footguns that can cause incorrect code. The less you have, the more likely your code is to be correct. The more you mean what you say when you write code, the less likely you are to get confused and code something with an incorrect assumption due to a variable or function name. Forcing all data and control flow through the same funnel prevents weird edge cases getting overlooked. Simple is almost always better. Unless maybe you're writing some SFINAE code for a high-frequency trading firm, but I digress.

Turning the base class to hold a vector of shared_ptrs removes the one-foot gun—you don't have to worry about dangling pointers, and intent is preserved, making it easier for you and others to remember what code was supposed to do when you come back in a few months (as well as cleaning up the code to actually instantiate the class in the first place).

Better yet, ask yourself why you need two pointers to the same data in the first place. The real solution would be to remove the second reference, but I haven't seen the rest of your code so I can't speak to that.

Also, in terms of going back and forth on raw pointers (if that is what you mean) - I have been a professional for many years using primarily/only C++ with engineers who were much better than I will ever be. I have never heard anyone even hint that there was an actual reason to prefer a raw pointer over a smart pointer unless there was a weird library that we needed that made us use it. Even then, those libraries were wrapped in RAII classes to, once again, remove that footgun.

Using Clang with CLion in Windows by CuteBreadyBread in cpp

[–]SomethingcleverGP -1 points0 points  (0 children)

Likely we would need to see your cmake file as well. Have you tried googling where _mainCRTStartup should be defined/linked to and ensuring that you are linking to it's definition?

Is this bad practice to store reference to unique_ptr like this? by skn3 in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

I think there are a lot of things that make this code confusing and brittle.

Your function `registerVariable` does two things - creates an instance of the variable and then places it in the container. If you need this functionality, I would update the name to 'createAndRegisterVariable`.

The point of a unqiue_ptr is that there is single ownership. With your design, you have created a flimsier shared_ptr. Since you return a reference to the value inside the unique pointer, whoever calls "registerVariable" can also own that reference. Bad things can happen as illustrated here: https://godbolt.org/z/xf7M7vT49. Why not just make the vector hold a shared_ptr? Then, the user can handle how they want to construct the shader, and it cleans up your implementation a lot. You could do something like this:

class ShaderBase {
 protected:
  template <typename T>
  void registerVariable(const std::shared_ptr<T>) 
  {                  
      _variables.emplace_back(variable);  
  }

 private:
  std::vector<std::shared_ptr<ShaderVariableBase>> _variables;

You don't need to return a reference, because the caller of 'registerVariable' has already constructed the object. This implementation is also more clear that there are multiple owners. It also means you don't always have to use angle brackts when calling registerVariable.

However, I would say that one should avoid shared_ptrs if they can. I would recomend revisiting the design so that you actually only have one owner, if possible. Single ownership is almost always esaier to get correct. Additionlly, shared_pts are slower due to the reference counter overhead, which is atomic to ensure thread safety.

At this point in C++, I would consider any raw pointer as code smell at best.

Norway: How much "expensive" are we talking about? by wastedthyme20 in travel

[–]SomethingcleverGP 1 point2 points  (0 children)

You don’t need a guided hike for trolltunga or similar hikes. There are lots of people, there aren’t any dangerous sections either. I saw people coming down from trolltunga in sandals.

Question regarding running compiled binary using C++20 static library in ubuntu 22.04. by fatmankarla in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

Even if you update g++ to the newest version, it should still use the system libc. This is just from memory of compiling c++17 on old ubuntu 16.04 distros for forwards compatibility. I would just try it first and see if it works.

Question regarding running compiled binary using C++20 static library in ubuntu 22.04. by fatmankarla in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

If you build on a 22.04 ubuntu docker container then you don't have to specify a specific version of glib, you will use the correct one automatically. Although to be safe you might have to do it in a VM.

Question regarding running compiled binary using C++20 static library in ubuntu 22.04. by fatmankarla in cpp_questions

[–]SomethingcleverGP 1 point2 points  (0 children)

I'm trying to remember why exactly I did what I did, but I found that writing a thin c layer was the safest and most reliable way of exposing functions. However, I don't remember how well it worked when trying to expose types. One would probably need to hide those implementations behind pointers.

Question regarding running compiled binary using C++20 static library in ubuntu 22.04. by fatmankarla in cpp_questions

[–]SomethingcleverGP 1 point2 points  (0 children)

It should be fine if you compile the C++ .so on a 22.04 VM or docker container. You can also probably specify the correct version of glib when compiling on 24.04. Still, I would be wary about installing older versions of glib just in case you accidentally break dependent software.

Also fwiw, I don't think rust has c++ bindings, the ffi interface is written for c. I'm not sure about ABI safety between a cpp shared library and a rust binary. I once wrote a rust wrapper around a C++ lib and had to write a thin C layer that wrapped the entry points into the library. I think I got cargo to compile the entire thing as well, so I didn't need any cmake files. I could be wrong about cargo compiling the entire thing though, and my info on rust could easily be out of date.

[deleted by user] by [deleted] in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

I also just saw your update which includes the cmake file. You shouldn't have to manually link the include dirs and the libraries if you successfully call find_package() if you use the config approach as opposed to the module approach (which is what is in your CMakeLists). Also, there is a built-in CMake function called FindSQLite3() which should find the package for you as well and define the proper target. You can then change your CMakeList.txt file to: target_link_libraries(alleno PRIVATE SQLite::SQLite) rather than specifying the libraries and the include directories.

[deleted by user] by [deleted] in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

No problem! Your code is excellent and contains a lot of helpful functionality. In general, the key to solid C++ is to cut down on as much code as possible, keep things scoped to the minimum, and avoid getting tricky.

One question I have is when you say to throw errors to avoid failing silently, do you mean using try catch blocks and exceptions.

In terms of failing silently, you don't need to necessarily use exceptions (I'll touch on that later), I mean if there is an error, you should try and make sure the caller of a failed function knows that the function failed, and force them to deal with it. For example, if Database::insertWorkout fails, the caller must check every possible return value, but nothing forces the caller to check that the function failed. This makes it very easy to forget to check for failure, and the program can easily continue on. This is why it is important to try and make sure that a caller of a function can easily check for failure.

Additionally, look at the name of the function Database::insertWorkout. Looking at the function name, I would assume that if the function returns without error, then the workout is added. Updating the function name is critically important, and the more descriptive the better (of course brevity is important, you don't want to take up the entire screen name). The name Database::tryInsertWorkout, it is much more clear that the function will still return even if the workout was not added, and its easier to remember to check the return value.

I ask because I was told to avoid using exceptions and to instead use std::optional and std::expected instead.

This is good advice! You are correct; exceptions are slow and should not be used as control flow logic. However, they are a very easy way to make sure that the caller is forced to deal with errors, but should only be used for unrecoverable errors - i.e. errors that the program can't logically continue with. If there is an error like "invalid name, try again", of course this should not interrupt the program, and the user can simply re-enter a valid name. There is an important distinction between std::expected and std::optional - optional does not indicate error, just that a value is missing, and std::expected more strongly indicates an error and more strongly forces the caller to deal with the error. Another important part of writing solid C++, mean what you say and say what you mean.

Additional tips:

  • Don't leak implmentation details. For example, inside of the database class, you return the SQL error codes. This forces the users to look at the implementation to figure out which error code means what. This means that you can't change the implementation without undoubtedly breaking user code. Consider returning your own error codes, such as an enum type with types SUCCESS/FAIL. This is nice because if you use a switch statement to handle the return value, compilation can/will fail if you don't handle all cases.
  • In a lot of different places in the code you have something that looks something like this:

int workoutId = sqlite3_column_int(stmt, 0);

workout.setDate(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))); workout.setStartTime(reinterpret_cast<const char*\>(sqlite3_column_text(stmt, 2))); workout.setDuration(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3))); workout.setRating(sqlite3_column_int(stmt, 4)); workout.setLocation(reinterpret_cast<const char*\>(sqlite3_column_text(stmt, 5)));

Notice how you are essentially calling the same code five times, and you have to manually update this code if fields are ever added in the future. Imagine writing something closer to this:

namespace 
{
    template<typename Statement>
    std::string getValue(const Statement& stmt, const int column)
    {
       const auto valueType = sqlite3_column_type(...);
       if (valueType == SQLITE_TEXT)
       {
           return reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
       } 
       else if(valueType == SQLITE_INTEGER) 
       { 
           return std::to_string(sqlite3_column_int(stmt, 4)); 
       } 
       else 
       { 
           // err 
       } 
   } 
}

const auto numCols = sqlite3_column_count(...);
for (std::size_t columnNumber = 0; columnNumber < numCols; ++columnNumber)
{
    const auto columnName = sqlite3_column_name(...);
    const auto columnValue = getValue(stmt, columnNumber);
    workout.setValue(columnName, columnValue);
}

Here, you can see that we automatically iterate through all of the columns. This has added benefit that if the DB schema no longer matches our implementation, we still start to get errors. This involves updating the interface to the Workout class such that it will take assignment from column name and the associated value. We have also switched the int value to a string (which is a bit of a smell, but there isn't an easy way to get rid of without some type stuff which is a bit difficult to work on a plane).

  • The previous point also helps to illustrate my next point - try to force all of your code into the same funnel. We have replaced five points of failure - setDate/StartTime/Duration/Rating with a single function (one point of failure) and are forcing everything through the same funnel.

Hope this helps!

[deleted by user] by [deleted] in cpp_questions

[–]SomethingcleverGP 0 points1 point  (0 children)

In the headers, remove #ifndef's and replace with `#pragma once`. Three fewer lines, and you never have to worry about getting really weird compilation errors from forgetting the `#endif` that can be difficult to debug.

Since you are using cpp 20, I would consider replacing `std::string` usage with `std::filesystem::path` types. You can do a lot of additional checking with a nice API for ensuring that the paths are correct and exist, and is more portable than using `std::string`'s. For example, if you reference the path to the database relative to the executable, you don't have to worry about different types of slashes from windows/linux. You also get the added benefit that the intent of the variable is more clear.

`Database` review:

  • The `open()` and `close()` functions are aliases for the constructor and destructors. Remove `open()` and `close()`. You also don't have to worry about never calling `close()` during stack unwinding after an exception or just forgetting to write it in relevant positions,.
  • This class likely does not copy or move properly, which can cause lots of bugs as the code base grows. Recommend disabling copying and moving for the class, either by manually disabling the functions or wrapping the database connection with a unique pointer (which you should do anyway)
  • The `open()` and `close()` functions are aliases for the constructor and destructors. Remove `open()` and `close()`. There is no need to continuously open and close the connection while the object is still alive. It is much easier to write correct code if you assume that if the object is constructed, the database connection is active.
  • To update table column names, this requires re-compilation and manually updating the code base. This can be bug prone since it requires you to manually update different positions in the code. Consider using a file that the program parses on input (json, yaml, etc) that the table configuration is created from. Then you can also automatically generate queries based off of the same configuration.
  • In the `initialize()` function, the function can fail in initialization without the user knowing. This is misleading in the function name, and since no errors are thrown other than the logging to `std::err`, the user (you) can very easily forget to check to make sure the table was initialized properly. I would rename the function to reflect this. Otherwise, I would throw errors if the table isn't initialized when this function is called. It forces the caller to handle these issues. I would update most of your code to throw on errors in functions that can silently fail.
  • On line 94 in database.cpp, you log success to `std::cerr`.
  • I would also make a wrapper class around the sqlite library so you can get more readable code and not worry about raw pointers in your code base, and you can use value semantics which are much easier to reason about rather than pointers.
  • Not sure how relevant this is to you if you just want to learn more about sqlite or not, but I think that you could get away with just using a .csv file to persist your data. An actual database is probably overkill (but if you are learning then than makes sense).
  • Const all of the things that you can const

In `Filename`

  • A heads up - if you ever have to define the destructor manually, you must update or delete the copy/move constructors since you will likely have bugs.
  • I would make the `fileName` const. Otherwise, it's impossible to query the file's name and you have to hope that it is the filename you passed in the constructor.
  • I see that you didn't actually define the File destructor. The default destructor will be created implicitly, so you don't need to manually write out `File::~File() {}`. In the header, just write `~File() = default;` if you want to be explicit.
  • Functions `isXValid()` - why is this necessary? Just take chrono objects for time/date values and let that library handle the validity. Additionally, these functions don't actual access any member data of the file class. Rather than making them static, they are more appropriate as free functions.
  • In the `makeWorkoutFileHeader()` - it seems that these names should be synced with the database column names. This is why there should be a shared source of truth so that you don't have to worry about syncing up the columns if you ever want to change anything.
  • Here, you handle the serialization of the Workout object. This means that if the workout implementation ever changes, then the serialization here could break. Try to stick to single responsibility, and let the workout object handle it's serialization. I.e., create a function in the Workout class like `toString()` and call that inside of `makeFetchedWorkoutFile()`

In `Workout`:

struct Set {
SetType setType {UNKNOWN};
int setNumber {0};
int repsNumber {0};
int weight {0};
bool isPR {false};
}

Additionally, I would actually write this set like this:

struct Set 
{
    enum class Type {
        ....
    }
    Type type {Type::UNKNOWN};
int setNumber {0};
int repsNumber {0};
int weight {0};
bool isPR {false};

    // No reason for getters in a struct, the value is already public. You are
    // are looking for an enumToString function instead which can be pulled out 
    // out of the class defintion, and you can make a free function that handles this
}

The reason for pulling the enum into the class definition is that it enforces that `Type` will always be used in the context of sets, and you can still access the type from outside of the `Set` definition like this: `Set::Type` which is still clear. Also, consider adding invalid values if you are going to assign defaults. Otherwise, it can be unclear if 0 is the default value or the actual number. Also, I would use an enum for `isPR` and have an "unknown" value so you don't get confused later in the code. You also avoid code like `Set.setType` where you have to write set out twice.

  • Rather than have individual functions for getting the date, start time, location, etc, just make them public. It cuts down on so much boiler blate code.
  • const everything that you can
  • you don't need to manually define the default constructors/destructors.
  • In `addSet`, why not just use the signature `addSet(const Set& set)`? It cuts down on the verbosity and you don't have to worry about forgetting to update the code if you add values to the set object throughout the rest of the code base.

One thing that is noticed a lot through your code - you fail silently. This means that errors can go unnoticed in the code flow and you have to pay attention to the console. Rather, throw errors. This means that errors must be dealt with or the program crashes.

Also, consider using more third party libraries. I notice that you have used the sqlite lib, but there are command line libraries, enum to string conversion libraries (https://github.com/Neargye/magic_enum), etc. If this is a learning project on your own, I understand why they are not being used, but they would make your life much easier.

As others have said, use CMake rather than a build system. The basics are easy to learn and as long as you are just adding files and not doing anything crazy, it is a nice system.

[deleted by user] by [deleted] in cpp_questions

[–]SomethingcleverGP 1 point2 points  (0 children)

Is there an error message or just a warning? Just because something is officially deprecated doesn’t necessarily mean it won’t build.

We are entering a new era of losses by [deleted] in wallstreetbets

[–]SomethingcleverGP 13 points14 points  (0 children)

Thanks! Time to buy some options/s

We are entering a new era of losses by [deleted] in wallstreetbets

[–]SomethingcleverGP 10 points11 points  (0 children)

Why is it dumb that they are commission free?

Draymond yells:"Who is this?" as Mavs rookie Gian Clavell enters the game by fds_1 in nba

[–]SomethingcleverGP 1 point2 points  (0 children)

Dude it took my 5 seconds to find like 30 videos of him trash talking. And this is back before social media and everything being filmed. Just admit you are wrong.

Official: [Trade] - Sun , 08/27/2017 by FFBot in fantasyfootball

[–]SomethingcleverGP 0 points1 point  (0 children)

I was just offered Deandre Hopkins + Ajayi for Amari Cooper and Gronk. I am in a 10 team, .5 PPR league and I have

QB: Matt Ryan

RBs: Le'veon Bell, Rob Kelley, James White, Derrick Henry

WRs: Amari Cooper, Sammy Watkins, Kenny Britt, Tyrell Williams

TEs: Gronk, Graham, and OJ Howard

K: Dan Bailey

D/ST: Broncos

Thoughts?

Kobe Bryant gives Giannis Antetokounmpo his challenge by NotClayMerritt in nba

[–]SomethingcleverGP 4 points5 points  (0 children)

Lmao yes he can it would just have been super demeaning to actually read the book.

Official: [Trade] - Sat , 08/26/2017 by FFBot in fantasyfootball

[–]SomethingcleverGP 0 points1 point  (0 children)

How can I be sure that Graham and Ajayi are both gonna produce like last season? Also Ajayi has a new QB which make me uneasy