I am implementing a parallel accumulator class in C++. The implementation of the class is as follows:
#include <iostream>
#include <thread>
#include <cstring>
#include "cblas.h"
class ParallelAccumulator {
public:
int num_contributions;
double** contributions;
int* is_contributing;
int num_elements;
ParallelAccumulator(int num_contributions, int num_elements) {
this->num_contributions = num_contributions;
this->num_elements = num_elements;
contributions = new double*[num_contributions];
is_contributing = new int[num_contributions];
for (int i = 0; i < num_contributions; i++) {
contributions[i] = new double[num_elements];
is_contributing[i] = 0;
}
}
void reset() {
for (int i = 0; i < num_contributions; i++) {
is_contributing[i] = 0;
}
}
void zero_contributions() {
for (int i = 0; i < num_contributions; i++) {
memset(contributions[i], 0, num_elements * sizeof(double));
}
}
int check_out_contribution() {
for (int i = 0; i < num_contributions; i++) {
if (is_contributing[i] == 0) {
is_contributing[i] = 1;
return i;
}
}
return -1;
}
void check_in_contribution(int contrib_index) {
is_contributing[contrib_index] = 0;
}
void reduce(double* output) {
for (int i = 0; i < num_contributions; i++) {
if (is_contributing[i] == 1) {
cblas_daxpy(num_elements, 1.0, contributions[i], 1, output, 1);
}
}
}
~ParallelAccumulator() {
for (int i = 0; i < num_contributions; i++) {
delete[] contributions[i];
}
delete[] contributions;
delete[] is_contributing;
}
};
However, I am having compilation issues when I create the threads to test the class as follows:
void test_function(ParallelAccumulator& accumulator, double* output, int id) {
int contrib_index = accumulator.check_out_contribution();
if (contrib_index == -1) {
std::cout << "Error: no available contrib arrays" << std::endl;
return;
}
double* contrib = accumulator.contributions[contrib_index];
for (int i = 0; i < accumulator.num_elements; i++) {
contrib[i] = id;
}
accumulator.check_in_contribution(contrib_index);
}
int main() {
int num_contributions = 4;
int num_elements = 10;
double output[num_elements];
ParallelAccumulator accumulator(num_contributions, num_elements);
/* problematic code start here */
std::thread t1(test_function, std::ref(accumulator), output, 1);
std::thread t2(test_function, std::ref(accumulator), output, 2);
std::thread t3(test_function, std::ref(accumulator), output, 3);
std::thread t4(test_function, std::ref(accumulator), output, 4);
/* problematic code end here */
t1.join();
t2.join();
t3.join();
t4.join();
accumulator.reduce(output);
for (int i = 0; i < num_elements; i++) {
std::cout << output[i] << " ";
}
std::cout << std::endl;
return 0;
}
The compilation errors are:
parallel_accumulator.cpp:87:67: error: no matching function for call to 'std::thread::thread(void (&)(ParallelAccumulator&, double*, int), std::reference_wrapper<ParallelAccumulator>, double [num_elements], int)' 87 | std::thread t1(test_function, std::ref(accumulator), output, 1);
| ^ In file included from /usr/local/Cellar/gcc/11.3.0_2/include/c++/11/thread:43,
from parallel_accumulator.cpp:2: /usr/local/Cellar/gcc/11.3.0_2/include/c++/11/bits/std_thread.h:127:7: note: candidate: 'template<class _Callable, class ... _Args, class> std::thread::thread(_Callable&&, _Args&& ...)' 127 | thread(_Callable&& __f, _Args&&... __args)
| ^~~~~~ /usr/local/Cellar/gcc/11.3.0_2/include/c++/11/bits/std_thread.h:127:7: note: template argument deduction/substitution failed: parallel_accumulator.cpp:87:67: note: variable-sized array type 'double (&)[num_elements]' is not a valid template argument 87 | std::thread t1(test_function, std::ref(accumulator), output, 1);
| ^ In file included from /usr/local/Cellar/gcc/11.3.0_2/include/c++/11/thread:43,
from parallel_accumulator.cpp:2: /usr/local/Cellar/gcc/11.3.0_2/include/c++/11/bits/std_thread.h:157:5: note: candidate: 'std::thread::thread(std::thread&&)' 157 | thread(thread&& __t) noexcept
| ^~~~~~ /usr/local/Cellar/gcc/11.3.0_2/include/c++/11/bits/std_thread.h:157:5: note: candidate expects 1 argument, 4 provided /usr/local/Cellar/gcc/11.3.0_2/include/c++/11/bits/std_thread.h:121:5: note: candidate: 'std::thread::thread()' 121 | thread() noexcept
= default;
| ^~~~~~
What is the proper syntax to fix the error? What modifications can I make to this implementation to make it work properly and have as much flexibility as possible?
Thanks a lot to all the participants in the discussion above, in particular to #doron. The code with the correct syntax is as follows:
constexpr int num_elements = 10;
std::thread t1(test_function, std::ref(accumulator), &output[0], 1);
std::thread t2(test_function, std::ref(accumulator), &output[0], 2);
std::thread t3(test_function, std::ref(accumulator), &output[0], 3);
std::thread t4(test_function, std::ref(accumulator), &output[0], 4);
There are several ways to call std::thread::thread(), when in doubt, use the simplest one using a lambda..
This sure isn't the most pedantic answer, but it does not penalise performance and always works.
// quick and efficient
std::thread t1([&]() { test_function(accumulator), &output[0], 1); });
// etc...
// If you do not like vague lambda captures
std::thread t1([&accumulator, &output]() { test_function(accumulator), &output[0], 1); });
// etc...
Related
I'm fairly new to C++, and I wanted to test what I know about it by implementing a mediocre List class.
List.hpp:
#ifndef LIST_HPP
#define LIST_HPP
// Only supports int lists up to 100 items for now.
class List
{
public:
List(int arr[], int length);
void append(int item);
void print();
void remove();
int len();
List operator+(List obj);
protected:
private:
int *list = new int[100];
int size = 0;
};
#endif
List.cpp:
#include <iostream>
#include "List.hpp"
List::List(int arr[], int length)
: size(length)
{
for (int i = 0; i < length; i++)
{
list[i] = arr[i];
}
}
void List::append(int item)
{
list[size] = item;
size++;
}
void List::print()
{
for (int i = 0; i < size; i++)
{
if (i == 0)
{
std::cout << '[';
}
std::cout << list[i];
if (i == size - 1)
{
std::cout << ']' << std::endl;
}
else
{
std::cout << ", ";
}
}
}
void List::remove()
{
if (size == 0) {
std::cout << "Error: list is empty" << std::endl;
return;
}
else {
for (int i = 0; i < size - 1; i++) {
list[i] = list[i + 1];
}
size--;
}
}
int List::len()
{
return size;
}
List List::operator+(List obj)
{
List res({}, 0);
for (int i = 0; i < this->size; i++)
res.append((this->list)[i]);
for (int i = 0; i < obj.size; i++)
res.append((obj.list)[i]);
res.size = this->size + obj.size;
return res;
}
However, upon initializing List mylist({1, 2, 3}, 3);, the compiler says,
main.cpp: In function 'int main()':
main.cpp:9:29: error: no matching function for call to 'List::List(<brace-enclosed initializer list>, int)'
List mylist({1, 2, 3}, 3);
^
In file included from main.cpp:3:0:
List.hpp:9:9: note: candidate: List::List(int*, int)
List(int arr[], int length);
^~~~
List.hpp:9:9: note: no known conversion for argument 1 from '<brace-enclosed initializer list>' to 'int*'
List.hpp:6:7: note: candidate: constexpr List::List(const List&)
class List
^~~~
List.hpp:6:7: note: candidate expects 1 argument, 2 provided
List.hpp:6:7: note: candidate: constexpr List::List(List&&)
List.hpp:6:7: note: candidate expects 1 argument, 2 provided
But when I intialize mylist using
int myarr[3] = {1, 2, 3};
List mylist(myarr, 3);
it works perfectly. Why is this so?
In List mylist({1, 2, 3}, 3);, {1, 2, 3} isn't an array, its a std::initializer_list.
I am trying to run a code that defines objects that are a collection of English letters.
I dont know why it does not compile.
I have tried to change from int to const int but it is not the case,
and also added the disable 4996 message but it didnt help.
#include <iostream>
using namespace std;
class CharSet
{
int size;
char* pSet;
public:
// -----------------------------------
CharSet(int const size, char* set)
{
this->size = size;
pSet = new char[strlen(set) + 1];
strcpy(pSet, set);
}
// -----------------------------------
~CharSet()
{
delete[] pSet;
}
// -----------------------------------
CharSet operator*(const CharSet & other)
{
int maxSize = 0;
if (this->size >= other.size)
maxSize = this->size;
else
maxSize = other.size;
char * ret = new char[maxSize + 1];
char temp;
int index = 0;
for (int i = 0; i < this->size; i++)
{
temp = this->pSet[i];
for (int j = 0; j < other.size; j++)
{
if (other.pSet[j] == temp)
{
ret[index] = temp;
index++;
}
}
}
ret[index] = '\0';
return CharSet(maxSize, ret);
}
// -----------------------------------
bool operator()(char check)
{
bool flag = false;
for (int i = 0; i < this->size; i++)
{
if (pSet[i] == check)
flag = true;
}
return flag;
}
// -----------------------------------
friend ostream& operator<<(ostream& os, const CharSet& s)
{
os << s.pSet;
return os;
}
// -----------------------------------
};
int main()
{
CharSet s1(4, "DAQP"), s2(3, "AXZ");
cout << s1 * s2 << endl;
if (s1('Q') == true)
cout << "The element is member of the set" << endl;
else
cout << "The element is not member of the set" << endl;
return 0;
}
errors:
E0289 no instance of constructor "CharSet::CharSet" matches the argument
E0289 no instance of constructor "CharSet::CharSet" matches the argument list
C4996 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
C2664 'CharSet::CharSet(const CharSet &)': cannot convert argument 2 from
C2664 'CharSet::CharSet(const CharSet &)': cannot convert argument 2 from 'const char [4]' to 'char *'
you need a const char* in your constructor:
CharSet(int const size, const char* set)
Thanks to
#holy black cat
"DAQP" is a const char[] which you didn't provide a constructor for that(the array will implicitly convert to pointer).
A better way is using std::string:
class CharSet
{
std::string pSet;
public:
// -----------------------------------
CharSet(std::string set) : pSet(set)
{
}
// -----------------------------------
~CharSet()
{
}
// -----------------------------------
CharSet operator*(const CharSet & other)
{
int maxSize = 0;
std::string ret;
char temp;
int index = 0;
for (int i = 0; i < pSet.size(); i++)
{
temp = pSet[i];
for (int j = 0; j < other.pSet.size(); j++)
{
if (other.pSet[j] == temp)
{
ret += temp;
index++;
}
}
}
return CharSet(ret);
}
// the rest of members ...
//
};
full code at godblot
Yesterday my friend challenged me to write a function in C which would return an array of function pointers where i-th function would return i.
It is easy to get a similar effect in C++, but I am not sure how to do it in C.
Can anyone help me with that?
Edit.
The effect that I am looking for is something equivalent to this.
vector <function<int()>> get_functions(int n) {
vector <function<int()>> functions;
for (int i = 0; i < n; ++i) {
functions.emplace_back([i]() {
return i;
});
}
return functions;
}
int main() {
auto functions = get_functions(10);
for (auto f:functions) {
cout << f() << endl;
}
return 0;
}
Edit.
As asked in the comment section I provide my poor attempt on the challenge.
typedef int (*fun_t)(void);
int fun() { return 0; }
int fun1() { return 1; }
fun_t *get_functions() {
fun_t *functions = malloc(sizeof(fun_t) * 2);
functions[0] = fun;
functions[1] = fun1;
return functions;
}
int main() {
fun_t* funs=get_functions();
for (int i = 0; i < 2; ++i) {
printf("%d\n",funs[i]());
}
free(funs);
}
The C++ code is cheating. function<int()> is not a function pointer; in fact, it's not a pointer at all, it's a class.
Therefore the equivalent C code would look something like this:
#include <stdio.h>
#include <stdlib.h>
// function<int ()>, simplified version just for this task
typedef struct {
int (*code)(int);
int ctx;
} function_int_t;
// function<int()>::operator()()
int call(function_int_t fun) {
return fun.code(fun.ctx);
}
// lambda body
int proto(int ctx) {
return ctx;
}
function_int_t *get_functions(size_t n) {
function_int_t *functions = calloc(n, sizeof *functions);
if (!functions) {
abort(); // hey, that's how C++ does it
}
for (size_t i = 0; i < n; i++) {
functions[i] = (function_int_t){ proto, i }; // capture i
}
return functions;
}
int main(void) {
size_t n = 10;
function_int_t *functions = get_functions(n);
for (size_t i = 0; i < n; i++) {
printf("%d\n", call(functions[i]));
}
free(functions);
return 0;
}
I am trying to come up with a generic buffer (or heap container - not sure how to call it) for generic objects. So I could have a large number of them in contiguous memory.
The header I did works, but I was wondering if you guys could help me understand what am I doing wrong if anything, or any gotchas/bugs I am missing here. Or any other re-factoring I could do to make it better.
buffer.h
#ifndef _ENGINE_BUFFER
#define _ENGINE_BUFFER
#include <cstdlib>
#include <cstdio>
#include <typeinfo>
#include <cstring>
// Defining buffer struct:
template <typename T>
struct Buffer {
unsigned int _size;
unsigned int _number_of_objects;
unsigned int _head;
unsigned int _next;
unsigned int * _backlog;
int _backlog_head;
T* _buffer_address;
};
// Creating buffer:
template <typename T>
Buffer<T>* createBuffer(int size) {
Buffer<T>* _buffer = (Buffer<Sprite>*) calloc(1, sizeof(Buffer<T>));
if (_buffer) {
*(_buffer) = Buffer<T>();
_buffer->_size = size;
_buffer->_number_of_objects = 0;
_buffer->_head = 0;
_buffer->_next = 0;
_buffer->_backlog = (unsigned int*) calloc(size, sizeof(int));
_buffer->_backlog_head = -1;
_buffer->_buffer_address = (T*) calloc(size, sizeof(T));
if(_buffer->_buffer_address) {
return _buffer;
}
}
return (Buffer<T>*) nullptr;
}
// Adding object to buffer:
template <typename T>
int addObjectToBuffer(Buffer<T>* _buffer, const T &_obj) {
int _head = 0;
if (_buffer->_backlog_head > -1) {
_buffer->_buffer_address[_buffer->_backlog[_buffer->_backlog_head]] = _obj;
_head = _buffer->_backlog_head;
_buffer->_backlog_head--;
_buffer->_number_of_objects++;
} else {
if ( (int) (_buffer->_number_of_objects + 1) >= _buffer->_size){
T* _new_address = (T*) calloc((_buffer->_number_of_objects + 1) * 2, sizeof(T));
memcpy(_new_address, _buffer->_buffer_address, _buffer->_number_of_objects * sizeof(T));
free(_buffer->_buffer_address);
_buffer->_buffer_address = _new_address;
_buffer->_size = (_buffer->_number_of_objects + 1) * 2;
for (int i = _buffer->_head; i < _buffer->_size; i++){
_buffer->_buffer_address[i] = 0;
}
}
_buffer->_buffer_address[_buffer->_head] = _obj;
_buffer->_number_of_objects++;
_head = _buffer->_head;
_buffer->_head++;
}
return _head;
}
// Getting number of objects in buffer:
template <typename T>
unsigned int bufferGetSize(Buffer<T>* _buffer) {
return (int) _buffer->_number_of_objects;
}
// Getting next object in buffer:
template <typename T>
T* bufferGetNext(Buffer<T>* _buffer) {
if (_buffer->_backlog_head > -1){
for (int i = 0; i <= _buffer->_backlog_head; i++ ) {
if (_buffer->_backlog[i] == _buffer->_next) _buffer->_next++;;
}
}
unsigned int _next = _buffer->_next;
if (_next < _buffer->_head) {
_buffer->_next++;
return &_buffer->_buffer_address[_next];
} else {
_buffer->_next = 0;
bufferGetNext(_buffer);
}
}
// Reset iterator head:
template <typename T>
void bufferResetHead(Buffer<T>* _buffer){
_buffer->_next = 0;
}
// Deleting object from buffer:
template <typename T>
void deleteObjectFromBuffer(Buffer<T>* _buffer, unsigned int _obj_index) {
if (_obj_index >= 0 && _obj_index <= _buffer->_head) {
bool _obj_exists = false;
for (int i = _buffer->_backlog_head; i >= 0; i-- ) {
if (_buffer->_backlog[i] == _obj_index){
_obj_exists = true;
fprintf(stderr, "\n[ Warning: object_deleted_twice | buffer: %#010x | object: %#010x | index: \"%d\" ]\n",
_buffer, &_buffer->_buffer_address[_obj_index], _obj_index);
}
}
if (!_obj_exists) {
_buffer->_backlog_head++;
_buffer->_backlog[_buffer->_backlog_head] = _obj_index;
_buffer->_number_of_objects--;
}
} else {
fprintf(stderr, "\n[ Warning: index_out_of_range | buffer: %#010x | index: \"%d\" ]\n",
_buffer, _obj_index);
}
}
// Deleting buffer:
template <typename T>
void deleteBuffer(Buffer<T>* _buffer) {
free(_buffer->_buffer_address);
free(_buffer);
}
// Print buffer memory:
template <typename T>
void printBufferMemory(Buffer<T>* _buffer, unsigned int _number_of_columns, bool _print_address) {
int _column_count = 0;
printf("\n");
printf("[ Number of objects in buffer: %#02d | Actual buffer size: %#02d ]\n", _buffer->_number_of_objects, _buffer->_size);
for (int i = 1; i < 11 * _number_of_columns; i++) {
printf("=");
}
printf("\n");
for (int i = 0; i < _buffer->_size; i++) {
if (_column_count > _number_of_columns - 1) {
printf("\n");
_column_count = 0;
}
if (_print_address) {
printf("%#010x ", &_buffer->_buffer_address[i]);
} else {
printf("%#010d ", *(&_buffer->_buffer_address[i]));
}
_column_count++;
}
printf("\n");
for (int i = 1; i < 11 * _number_of_columns; i++) {
printf("=");
}
printf("\n");
}
#endif // _ENGINE_BUFFER
sprite.h
#ifndef _SPRITE
#define _SPRITE
struct Sprite {
int value = 0;
Sprite(int n){
value = n;
}
};
#endif // _SPRITE
main.cpp
#include <cstdio>
#include <cstdlib>
#include "sprite.h"
#include "buffer.h"
using namespace std;
int main()
{
int buffer_size = 512;
int object_number = 512;
Buffer<Sprite>* engine_buffer = createBuffer<Sprite>(buffer_size);
for (int i = 0; i < object_number; i++) {
addObjectToBuffer(engine_buffer, Sprite(i + 100));
}
for (int i = 0; i < bufferGetSize(engine_buffer); i++) {
printf("Value of Sprite %d is: %d\n", i + 1, bufferGetNext(engine_buffer)->value);
}
printBufferMemory(engine_buffer, 10, false);
deleteBuffer(engine_buffer);
return(0);
}
The following function seems to be the cause of the problem"
template <class Type>
void * server_work(void * arg)
{
int ii;
int cno;
Server<Type> * server = (Server<Type> *) arg;
Customer<Type> newcust;
for(ii=0; ii<QUEUE_LENGTH; ii++)
{
size_t length = rand()%(MAX_RANGE-MIN_RANGE)+MIN_RANGE ; // Generate the number, assign to variable.
pthread_mutex_lock(&MUTEX);
cno=CUSTOMER_COUNT;
CUSTOMER_COUNT++;
pthread_mutex_unlock(&MUTEX);
newcust=Customer<Type>(cno, cno,cno,length);
if(CUSTOMER_COUNT<=QUEUE_LENGTH)
{
server->IncreaseNumOfCustomers();
for(size_t i = 0; i < length; ++i)
{
newcust.getLinkedList().insertFirst(1000);
}
server->getCustomers()[ii]=newcust;
}
else
{
break;
}
}
return NULL;
}
The problem occurs when the compiler reads the following bit of code:
int main(int argc, char** argv)
{
pthread_t threads[NUMBER_OF_SERVERS];
int i,j;
if(pthread_mutex_init(&MUTEX, NULL))
{
cout<<"Unable to initialize a MUTEX"<<endl;
return -1;
}
Server<int> servs[NUMBER_OF_SERVERS];
for(i = 0; i < NUMBER_OF_SERVERS; i++)
{
servs[i].setServerNum(i);
pthread_create(threads+i, NULL, server_work, (void *)&servs[i]);//<<--compiler flags here
}
// Synchronization point
for(i = 0; i < NUMBER_OF_SERVERS; i++)
{
pthread_join(*(threads+i), NULL);
}
cout<<"SERVER-NO\tCUSTOMER-NO\tARRIVAL-TIME\tWAITING-TIME\tTRANSACTION-TIME"<<endl;
for(i = 0; i < NUMBER_OF_SERVERS; i++)
{
for(j=0; j<servs[i].getCustomersServed(); j++)
{
cout<<i<<"\t\t"<<servs[i].getCustomers()[j].getCustomerNumber()<<"\t\t"<<servs[i].getCustomers()[j].getArrivalTime()<<"\t\t"<<servs[i].getCustomers()[j].getWaitingTime()<<"\t\t"<<servs[i].getCustomers()[j].getTransactionTime()<<endl;
}
}
cout<<endl;
cout<<endl;
I get the following error from the compiler:
main.cpp:84:71: error: no matches converting function ‘server_work’ to type ‘void* ()(void)’
main.cpp:26:8: error: candidate is: template void* server_work(void*)
You have wrong prototype:
template <class Type>
void * server_work(void * arg)
While pthread is expecting something like this
void * server_work(void * arg)
However it's not so hard to workaround this, for instance:
void* CoolWrapper(void* arg)
{
return server_work<desired_type>(arg);
}