C++

1755 readers
2 users here now

The center for all discussion and news regarding C++.

Rules

founded 1 year ago
MODERATORS
226
 
 

cross-posted from: https://lemmy.world/post/1736237

Just wanted to share my C++20 thread pool implementation. Based on my testing, it's one of the fastest implementations out there with a decent feature set. Development work is ongoing and I'm always looking for more suggestions and contributors!

227
228
7
submitted 1 year ago* (last edited 1 year ago) by [email protected] to c/cpp
 
 

I have this code where I have a class MyElement that holds a MyData<UnderlyingData> that's constructed during the constructor:

#include <atomic>
#include <iostream>
#include <vector>

struct UnderlyingData {
  int a;
  int b;

  UnderlyingData(int _a, int _b) : a(_a), b(_b) {}
};

template <typename T> class MyData {
public:
  std::atomic<T> data_;

  template <typename... Args>
  MyData(Args &&...args) : data_(T(std::forward<Args>(args)...)) {}
};

class MyElement {
public:
  int c;
  MyData<UnderlyingData> mydata;

  explicit MyElement(int _c) : c(_c), mydata(0, 6) {}
};

int main() {
  std::vector<MyElement> arr;
  arr.emplace_back(5);

  std::cout << arr.at(0).c << " " << arr.at(0).mydata.data_.load().a << " "
            << arr.at(0).mydata.data_.load().b << "\n";

  return 0;
}

As you can see, MyData<UnderlyingData> holds an atomic<UnderlyingData> and the constructor of MyData attempts to perfect forward to the UnderlyingData through the atomic. This code works if I try to construct a MyData<UnderlyingData> variable manually, or a MyElement variable, like this:

  MyElement element(5);
  std::cout << element.c << " " << element.mydata.data_.load().b << "\n";

However, when I put MyElement into a vector and use either emplace_back or push_back(MyElement(5)), I get this cryptic error:

test.cpp: In instantiation of ‘MyData<T>::MyData(Args&& ...) [with Args = {MyData<UnderlyingData>}; T = UnderlyingData]’:
test.cpp:20:7:   required from ‘void std::_Construct(_Tp*, _Args&& ...) [with _Tp = MyElement; _Args = {MyElement}]’
/usr/include/c++/11/bits/stl_uninitialized.h:92:18:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<MyElement*>; _ForwardIterator = MyElement*; bool _TrivialValueTypes = false]’
/usr/include/c++/11/bits/stl_uninitialized.h:151:15:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<MyElement*>; _ForwardIterator = MyElement*]’
/usr/include/c++/11/bits/stl_uninitialized.h:333:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<MyElement*>; _ForwardIterator = MyElement*; _Tp = MyElement]’
/usr/include/c++/11/bits/stl_uninitialized.h:355:2:   required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = MyElement*; _ForwardIterator = MyElement*; _Allocator = std::allocator<MyElement>]’
/usr/include/c++/11/bits/vector.tcc:474:3:   required from ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {int}; _Tp = MyElement; _Alloc = std::allocator<MyElement>; std::vector<_Tp, _Alloc>::iterator = std::vector<MyElement>::iterator]’
/usr/include/c++/11/bits/vector.tcc:121:21:   required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int}; _Tp = MyElement; _Alloc = std::allocator<MyElement>; std::vector<_Tp, _Alloc>::reference = MyElement&]’
test.cpp:30:19:   required from here
test.cpp:17:34: error: no matching function for call to ‘UnderlyingData::UnderlyingData(MyData<UnderlyingData>)’
   17 |   MyData(Args &&...args) : data_(T(std::forward<Args>(args)...)) {}
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:9:3: note: candidate: ‘UnderlyingData::UnderlyingData(int, int)’
    9 |   UnderlyingData(int _a, int _b) : a(_a), b(_b) {}
      |   ^~~~~~~~~~~~~~
test.cpp:9:3: note:   candidate expects 2 arguments, 1 provided
test.cpp:5:8: note: candidate: ‘constexpr UnderlyingData::UnderlyingData(const UnderlyingData&)’
    5 | struct UnderlyingData {
      |        ^~~~~~~~~~~~~~
test.cpp:5:8: note:   no known conversion for argument 1 from ‘MyData<UnderlyingData>’ to ‘const UnderlyingData&’
test.cpp:5:8: note: candidate: ‘constexpr UnderlyingData::UnderlyingData(UnderlyingData&&)’
test.cpp:5:8: note:   no known conversion for argument 1 from ‘MyData<UnderlyingData>’ to ‘UnderlyingData&&’

I've tried a number of permutations and can't seem to figure it out either. I don't understand why the constructor inferred seems to be UnderlyingData::UnderlyingData(MyData<UnderlyingData>) as I'm just passing two numbers to it... Any advice I can get is much appreciated.

229
 
 

Pretty interesting talks, especially focusing on safety.

230
 
 

Since c++11 it has been possible that instead of declaring your function as "int name(arguments);" you can now do "auto name(arguments) -> int;". The place I work at has it as style rule that all functions need to be declared that way. Now obviously this is not that large of a thing, and a consistent style is more important than my opinion here. But this has always felt like a weird thing, adding extra bloat to reading code. Anyways looking around I saw some positives to this construction, generally with the use of long return types, that are paramount when using templates. Here the benefit is that the function name is not hidden behind multiple template declarations, which does seem like a good argument. Also lamndbas generally use this. However I personally see some negatives here with using this foe every function, namely:

  • extra bloat when typing/reading the code. This however could be automated to switch between the needed representations. It currently isent so I personally have ti type the auto and trailing return type manually, its not a lot, but still. Also reading code has become a bit more annoying if you have a lot of function overrides as you now have to first look which block of declarations have the smae function name and then parse which one has the correct return type.
  • inconsistency with other typed programming languages: This one is probably why it irks me, but (and correct me on this) I dont know of a c style typed programming language that supports this type of syntax. Python has typehints(which you should use, please), which are declared after the function, and I remember Haskell also has their return type after the function name. But both of thede languages serve a different function than c++. More similar languages like c# and java dont support trailing return types.

Anyway enough of me ranting, I would like to know wath the other opinions here are. And whether this rant is missing se important arguments?

231
3
submitted 1 year ago* (last edited 1 year ago) by [email protected] to c/cpp
 
 

Sorry for writing this shitty guide but it's an issue that doesn't seem to be answered on the internet, and I really needed a recent version of clang++ on macOS for a new job. Feel free to make fun of me if there is an easier way to do it.

  1. Install Nix (usually used for reproducible builds but you can use it too for virtual environments): https://nixos.org/download.html#nix-install-macos

  2. Create a file called shell.nix at the root of your project, and put the following code inside:

{ pkgs ? import <nixpkgs> {} }:
  pkgs.mkShell {
    nativeBuildInputs = with pkgs.buildPackages; [ llvmPackages_16.clangUseLLVM ];
}
  1. Run nix-shell

  2. Check that you have the good version with the command clang++ -v

It's not a perfect solution, but it's mostly automated, you'll be able learn various topics of C++20 with this, and the stupid "clang 11" from Apple is not hijacking the latest version anymore.

232
 
 

Hello! I'm trying to inject an external CSS file into a website displayed in a QWebEngineView. I successfully injected one with

QString css = readFromFileInAnyWayYouLike();
QString jsscript = QString(R"( 
	(function() { 
		css = document.createElement('style'); 
		css.type = 'text/css'; 
		css.id = 'userContent';
		document.head.appendChild(css); 
		css.innerText = '${css}';
	})())").replace("${css}", css);
m_webEngine->page()->runJavaScript(jsscript);

but I don't like this solution at all, because it would allow a malicious css file to execute arbitary code. So I tried injecting giving the style element an src to a remote file, or assigning @import "http://localhost:8000/userContent.css"; to the css inner text, but both these methods were blocked by the CSP. researches on the web didn't help me at all sadly. Is this possible to inject an external css fine into the webpage in a "secure" way?

thanks in advance!

233
234
235
 
 

Hey all, teaching myself CPP through a few books (and a little bit of CS in general through CS50) and hit a road block.

I understand what pointers are, and understand they’re a big part of programming. My question is why?

What do you use pointers for? Why is forwarding to a new memory address preferable to just changing the variable/replacing what’s already at the memory address or at a new one? Is it because new data could exceed the size of that address already allocated?

Thanks in advance!

236
4
Inside boost::concurrent_flat_map (bannalia.blogspot.com)
submitted 1 year ago by gracicot to c/cpp
237
 
 

I want to free my code from the 5 std::mutex::unlock calls per function in favor of std::lock_guard. But I have the problem, that I have to keep the mutex locked when entering asynchronous callbacks.

Take this code for example:

std::map<std::size_t, std::set<std::size_t>> my_map;

size_t bar1 = ...;
size_t bar2 = ...;

std::lock_guard guard(my_map); // lock the map
my_map[p].insert(10);
foo(bar1, [&my_map](const auto& p){
    my_map[p].insert(10);

    // here we can unlock
});

foo is computing sth and then asynchronously calling the given lambda function and passing a parameter e to it. I need my_map to be locked the whole time. Keep in mind, that this is just an code example which might not map the real problem, so please don't optimize my given code.

238
8
Before std::thread (lemmy.world)
submitted 1 year ago by [email protected] to c/cpp
 
 

I'm working on a project that makes heavy use of multithreading; I think it's been years since I wrote code that didn't at least use std::thread or std::async to some extent.

I started programming in C for bare-metal AVR microcontrollers (no threading needed in that case), and I didn't really move into C++ until just after C++11 was already established; that is to say, I have always had access to concurrency tools that are built into the standard library.

I'm curious how threads were implemented prior to C++11; I know that 3rd-party libraries exist for this, but how did the libraries themselves handle it? I'm assuming the only option was to use calls to the OS with a lot of preprocessor macros depending on the target OS. Writing loops with a stored state would work, but not only did coroutines not exist in the STL until much later, but this wouldn't take advantage of multi-core CPUs, which were already commonplace before C++11.

There are certainly some times I take modern language features for granted. So, for the experienced programmers out there: How did it used to be done?

239
 
 

My implementation of the move-and-pop idiom for single, ranged and erase_if erasures respectively: https://plflib.org/reorderase.htm

Performance: Single erasures: 1010566% faster than .erase(position) on average (ie. 10157 x original speed), with a minimum of 10% faster for a vector of 10 ints, and a maximum of 25974010% faster for 870000 490-byte structs.

Ranged erasures: 12570% faster than .erase(first, last) on average, with a minimum of 20% faster for a vector of 10 ints, and a maximum of 77679% for 920000 490-byte structs.

std::erase_if/std::erase-style erasures: Not quite as incredible, this typically only outperforms the std:: equivalents on types larger than 25 bytes. 38% faster on average, with a minimum of 10% slower for 50 doubles, and a maximum of 180% for 920000 490-byte structs.

Full benchmarks + more detailed stats breakdowns are on the page.

240
6
Johan Berg: Empty Objects (www.youtube.com)
submitted 1 year ago by [email protected] to c/cpp
 
 

Using unique_ptr as an example, the presenter talks briefly about the empty object problem, ie when classes have non-zero size despite being empty, and then presents two ways to deal with that:

  • Empty Base Class Optimization (EBCO): exploits the fact that if a base class is empty, it won't increase the size of the derived class. The presenter uses this trick through the cool compressed_pair to have a unique_ptr implementation where the deleter doesn't take an extra byte;
  • [[no_unique_address]] which is a C++20 feature.
241
242
243
244
245
6
Ranges (C++20) (euroquis.nl)
submitted 1 year ago by [email protected] to c/cpp
246
 
 

cross-posted from: https://programming.dev/post/218925

I'm a back-end developer who wants to play with some desktop/front-end development.

I found a C++ library and started thinking what would be the best way to wrap this library in a way so that I can create a GUI in either C# or in Typescript.

What would you recommend?

Note: I'm trying to keep the question relatively vague because I want to get some ideas to try instead of finding the solution to my specific problem. I want to learn more about how others would approach such a problem. But I can provide more details, if necessary.

247
248
7
submitted 1 year ago by cgtjsiwy to c/cpp
 
 

P2881 proposes a new way to build iterators, similar to generators but with less performance overhead and better nesting (and hence composability):

struct generator123
{
    auto operator()(auto&& sink) const
    {
        std::control_flow flow;

        // With P2561: sink(1)??;
        flow = sink(1);
        if (!flow) return flow;

        // With P2561: sink(2)??;
        flow = sink(2);
        if (!flow) return flow;

        return sink(3);
    }
};

int main() {
    for (int x : generator123()) // x iterates 1, 2, 3
    {
        std::print("{}\n", x);
        if (x == 2)
            break;
    }
}

While the benefits are undisputable, it's quite verbose without P2561 and I feel C++ is starting to have too many different iterator models.

What do you think?

One of the authors has a nice presentation about pitfalls of ye olde for loops, which was probably a big motivator too: https://www.youtube.com/watch?v=95uT0RhMGwA

249
 
 

C++ in safety critical domain. How to approach type safety in particular when it comes to safety critical domains e.g. Automotive. Challenges in domain, beyond C++14 solutions.

Slides: https://github.com/italiancpp/itcppcon23

250
view more: ‹ prev next ›