lock free concurrency example; why is it not safe? - c++

I am trying to learn about concurrency in C++ and in doing so I am experimenting to see what works and what does not work. The example below is not well designed and I know there are much better ways of designing it but I would like to know why it seems that thread 1 and thread 2 are able to overwrite each other in the shared array. I thought the operations to the shared flag_atomic variable with the acquire/release semantics above and below the loading and writing of the shared idx_atomic index would prevent thread 1 and thread 2 retrieving the same index values regardless of the idx_atomic operation memory tags?
For reference I am using MSVC and x64.
#include <iostream>
#include <vector>
#include <atomic>
#include <thread>
#include <chrono>
using namespace std::chrono; // for ""ms operator
const size_t c_size = 40;
std::vector<int> shared_array;
std::atomic<bool> sync_start_atomic = false;
std::atomic<bool> flag_atomic = false;
std::atomic<size_t> idx_atomic = 0;
void thread1_x() {
bool expected_flag = false;
size_t temp_idx = 0;
while (!sync_start_atomic.load(std::memory_order_relaxed));
for (size_t i = 0; i < (c_size / 2); ++i) {
while (flag_atomic.compare_exchange_weak(expected_flag, true, std::memory_order_acq_rel, std::memory_order_acquire)) {
expected_flag = false;
}
temp_idx = idx_atomic.load(std::memory_order_relaxed);
idx_atomic.store((temp_idx + 1), std::memory_order_relaxed);
flag_atomic.store(false, std::memory_order_release);
shared_array[temp_idx] = i;
}
}
void thread2_x() {
bool expected_flag = false;
size_t temp_idx = 0;
while (!sync_start_atomic.load(std::memory_order_relaxed));
for (size_t i = 0; i < (c_size / 2); ++i) {
while (flag_atomic.compare_exchange_weak(expected_flag, true, std::memory_order_acq_rel, std::memory_order_acquire)) {
expected_flag = false;
}
temp_idx = idx_atomic.load(std::memory_order_relaxed);
idx_atomic.store((temp_idx + 1), std::memory_order_relaxed);
flag_atomic.store(false, std::memory_order_release);
shared_array[temp_idx] = i + 100;
}
}
void main(){
shared_array.reserve(c_size);
shared_array.assign(c_size, 0);
std::thread tn_1(thread1_x);
std::thread tn_2(thread2_x);
std::this_thread::sleep_for(60ms);
sync_start_atomic.store(true, std::memory_order_relaxed);
tn_1.join();
tn_2.join();
for (size_t i = 0; i < c_size; ++i) {
std::cout << shared_array[i] << " ";
}
std::cout << "\n";
}
Example real output:
100, 1, 101, 2, 3, 102, 4, 103, 104, 6, 106, 8, 108, 9, 10, 109, 11, 110, 12, 111, 14, 112, 113, 16, 17, 18, 115, 19, 116, 117, 118, 119, 0, 0, 0, 0, 0, 0, 0, 0.
Example expected output:
0, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 1, 2, 114, 3, 115, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 116, 16, 117, 17, 118, 18, 119, 19.

Your example output indicates that both threads are accessing the idx_atomic concurrently, which indicates a problem with your flag_atomic loop. The condition check you are using is backwards. compare_exchange_weak will return the result of the flag_atomic == expected_flag comparison - in other words, it returns true when the value is updated. Since you want to exit the loop when this happens, the comparison should be
while (!flag_atomic.compare_exchange_weak(expected_flag, true, std::memory_order_acq_rel, std::memory_order_acquire))

Related

Compile-Time Base64 Decoding in C++

Is it possible to decode base64 encoded data to binary data at compile-time?
I think of something that looks like this:
constexpr auto decoded = decodeBase64<"SGVsbG8=">();
or
constexpr auto decoded = decodeBase64("SGVsbG8=");
I have no special requirements fo the resulting type of decoded.
I found it surprisingly hard to google for a constexpr base64 decoder, so I adapted the one here:
https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594
Since that's MIT licensed, (sigh), be sure to slap this somewhere in the source file:
/**
* The MIT License (MIT)
* Copyright (c) 2016 tomykaira
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
To return a string from a constexpr function, you need to return a char array. Because you can't return an array or std::string, an std::array is the best option. But there is a problem - due to a standards oversight, until C++17 the [] operator of std::array is non-const. You can work around that by inheriting and adding a constructor though:
template <size_t N>
struct fixed_string : std::array<char, N> {
constexpr fixed_string(const char (&input)[N]) : fixed_string(input, std::make_index_sequence<N>{}) {}
template <size_t... Is>
constexpr fixed_string(const char (&input)[N], std::index_sequence<Is...>) : std::array<char, N>{ input[Is]... } {}
};
Change the decoder to use that instead of std::string, and it seems to work as constexpr. Requires C++14 because C++11 constexpr functions can only have one return statement:
template <size_t N>
constexpr const std::array<char, ((((N-1) >> 2) * 3) + 1)> decode(const char(&input)[N]) {
constexpr unsigned char kDecodingTable[] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};
static_assert(((N-1) & 3) == 0, "Input data size is not a multiple of 4");
char out[(((N-1) >> 2) * 3) + 1] {0};
size_t out_len = (N-1) / 4 * 3;
if (input[(N-1) - 1] == '=') out_len--;
if (input[(N-1) - 2] == '=') out_len--;
for (size_t i = 0, j = 0; i < N-1;) {
uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);
if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF;
if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF;
if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF;
}
return fixed_string<(((N-1) >> 2) * 3) + 1>(out);
}
Usage:
constexpr auto x = decode("aGVsbG8gd29ybGQ=");
/*...*/
printf(x.data()); // hello world
Demo: https://godbolt.org/z/HFdk6Z
updated to address helpful feedback from Marek R and Frank
parktomatomi's answer helped a lot to find this solution.
Using C++17 and std::array this seems to work.
The base64 decoder is based on the answer https://stackoverflow.com/a/34571089/3158571
constexpr size_t decodeBase64Length(const char *s)
{
size_t len = std::char_traits<char>::length(s);
if (s[len - 2] == '=')
return (len / 4) * 3 - 2;
else if(s[len -1] == '=')
return (len / 4) * 3 - 1;
else
return (len / 4) * 3 ;
}
constexpr std::array<int, 256> prepareBase64DecodeTable() {
std::array<int, 256> T{ 0 }; // breaks constexpr: T.fill(-1) or missing initialization
for (int i = 0; i < 256; i++)
T[i] = -1;
for (int i = 0; i < 64; i++)
T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
return T;
}
// based on https://stackoverflow.com/a/34571089/3158571
template<int N>
constexpr std::array<std::byte, N> decodeBase64(const char *b64Str)
{
constexpr auto T = prepareBase64DecodeTable();
std::array<std::byte, N> out = { std::byte(0) };
int valb = -8;
for (size_t i = 0, val = 0, posOut = 0; i < std::char_traits<char>::length(b64Str) && T[b64Str[i]] != -1; i++) {
val = (val << 6) + T[b64Str[i]];
valb += 6;
if (valb >= 0) {
out[posOut++] = std::byte((val >> valb) & 0xFF);
valb -= 8;
}
}
return out;
}
Usage is not perfect as I can not deduce the length of the resulting array without passing it explicitly as template parameter:
#define B64c "SGVsbG8xMg=="
constexpr auto b64 = decodeBase64<decodeBase64Length(B64c)>(B64c); // array<byte,7>
Demo at https://godbolt.org/z/-DX2-m

illegal, left operand has type 'DWORD [29]'

I'm pretty new to C++ and I'm really stuck here.
if (bAk == 1)
{
int fireRate = 134;
if (shotTiming < 30)
{
int valueX = (AssaultRifle::recoilTableX[shotTiming] * 0.48) + shakerNum;
int smoothingX = valueX / 5;
int valueY = (AssaultRifle::recoilTableY[shotTiming] * 0.48) + shakerNum;
int smoothingY = valueY / 5;
Sleep(3);
for (int i = 0; i < 5; i++)
{
mouse_move(valueX, valueY);
Sleep(fireRate / 5);
}
shotTiming++;
cout << valueX;
}
}
The only build error I am getting at this point is illegal, left operand has type 'DWORD [29]' Both int values of recoilTable are saying that the AssaultRifle namespace must have arithmetic or unscoped enum type. I just need to be put in the right direction of finishing it.
#pragma once
#include <Windows.h>
namespace AssaultRifle
{
const size_t MAX_INDEX_WEAPON = 1;
const size_t MAX_INDEX_RECOIL = 29;
DWORD recoilTableY[MAX_INDEX_WEAPON][MAX_INDEX_RECOIL] = {
{ 40, 48, 48, 48, 33, 33, 28, 24, 16, 13, 18, 22, 24, 29, 33, 33, 33, 29, 22, 20, 17, 17, 17, 17, 20, 27, 27, 27, 26 }
};
DWORD recoilTableX[MAX_INDEX_WEAPON][MAX_INDEX_RECOIL] = {
{ -36, 5, -59, -49, 3, 20, 25, 45, 43, 32, 82, 8, 43, -32, -25, -40, -35, -32, -43 , -42, -42, -55, -25, 15, 20, 35, 50, 62, 40 }
};
}
Your recoilTableX and recoilTableY arrays are both 2-dimensional 1:
DWORD recoilTableY[<# elements in first dimension>][<# elements in second dimension>] = {...};
DWORD recoilTableX[<# elements in first dimension>][<# elements in second dimension>] = {...};
But when reading individual values from the arrays, your code is indexing into only the first dimension. That is why you are getting the error, as you can't access an entire array as a single integer like you are attempting to do. You have to specify indexes for ALL of the available dimensions.
Change this:
AssaultRifle::recoilTableX[shotTiming]
AssaultRifle::recoilTableY[shotTiming]
To this instead:
AssaultRifle::recoilTableX[0][shotTiming]
AssaultRifle::recoilTableY[0][shotTiming]
MAX_INDEX_WEAPON is 1, so there is only 1 slot in the first dimension of the arrays, so the ONLY valid index in the first dimension is 0, which makes the first dimension pretty useless and should be removed, unless you are planning on adding values for additional weapons in the future.
MAX_INDEX_RECOIL is 29, so there are 29 slots in the second dimension of the arrays, so the ONLY valid indexes in the second dimension are 0..28 inclusive, but your code allows index 29 to be accessed.
The NAMES of your MAX_INDEX_WEAPON and MAX_INDEX_RECOIL constants are misleading, as they are not actually being used as indexes at all.
1: also, your arrays should be declared as const.
Try this instead:
#pragma once
#include <Windows.h>
namespace AssaultRifle
{
const size_t MAX_WEAPONS = 1;
const size_t MAX_RECOILS = 29;
const int recoilTableY[MAX_WEAPONS][MAX_RECOILS] = {
{ { 40, 48, 48, 48, 33, 33, 28, 24, 16, 13, 18, 22, 24, 29, 33, 33, 33, 29, 22, 20, 17, 17, 17, 17, 20, 27, 27, 27, 26 } }
};
const int recoilTableX[MAX_WEAPONS][MAX_RECOILS] = {
{ { -36, 5, -59, -49, 3, 20, 25, 45, 43, 32, 82, 8, 43, -32, -25, -40, -35, -32, -43 , -42, -42, -55, -25, 15, 20, 35, 50, 62, 40 } }
};
}
if (bAk == 1)
{
int fireRate = 134;
if (shotTiming < AssaultRifle::MAX_RECOILS)
{
int valueX = (AssaultRifle::recoilTableX[0][shotTiming] * 0.48) + shakerNum;
int smoothingX = valueX / 5;
int valueY = (AssaultRifle::recoilTableY[0][shotTiming] * 0.48) + shakerNum;
int smoothingY = valueY / 5;
Sleep(3);
for (int i = 0; i < 5; i++)
{
mouse_move(valueX, valueY);
Sleep(fireRate / 5);
}
shotTiming++;
cout << valueX;
}
}

Tests finish with exit code 139 (interrupted by signal 11: SIGSEGV)

I'm a CS student and one of the classes I'm taking this semester requires me to complete different assignments (in C++) and then test them with unit-tests written by our professor.
Link to the repository with tests and the skeleton of the exercises
All of the tests form la1 worked just fine. Sadly most of them stopped working just as I started testing code from lab2 exercises. All the tests print the same message to the console:
Running main() from gtest_main.cc
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
Today, after I've completed the lab2->greatestproduct exercise
Given an array of integers, write a function, maxProductFinderK, that can be obtained from any k integers in the array.
I checked all of the tests by hand and the result I've gotten were all correct so I don't think my code is the real issue here.
Is there anything that I could double-check to solve this problem? So far I've tried:
creating new CMakeLists.txt file (I don't think that's the problem since I use a template to create them)
cloning the repository with exercises again
Using different solutions just to make sure the code itself is not the problem
I'm running all the tests in CLion (with the latest update) on MacOS 10.13.3 if that's relevant.
// Edit: just as requested I added the code here in case the link will stop working.
Solution to the assignment:
int GreatestProduct(const std::vector<int> &numbers, int k) {
if ( numbers.size() > 0) {
std::vector<int> largestK = numbers;
std::sort(largestK.begin(), largestK.end());
if (k == 0) {
return 0;
} else if (k == 1) {
return largestK.at(largestK.size() - 1);
}
largestK.erase(largestK.begin(), largestK.end() - (k)); // remove all but k largest elements
std::vector<int> smallestK = numbers;
std::sort(smallestK.begin(), smallestK.end());
smallestK.erase(smallestK.begin(),
smallestK.begin() + k - (k % 2)); // remove all but k-(k%2) smallest elements
int mustContain = (k % 2) * (largestK.at(0) - 1) + 1;
//make sure that largestK and smallestK are the same size
if (k % 2 != 0) {
largestK.erase(largestK.begin());
}
std::vector<int> final;
int currentLargest = 0;
int currentSmallest = 0;
int greatestProduct = 1;
while (currentLargest + currentSmallest < k - (k % 2)) {
int firstOption = largestK.at(currentLargest) * largestK.at(currentLargest + 1);
int secondOption = smallestK.at(currentSmallest) * smallestK.at(currentSmallest + 1);
if (mustContain * firstOption > mustContain * secondOption) {
final.push_back(firstOption);
currentLargest += 2;
} else {
final.push_back(secondOption);
currentSmallest += 2;
}
}
for (int element : final) {
greatestProduct *= element;
}
return greatestProduct;
} else {
return 0;
} }
First out of 3 test. This one's the easiest since it always assumes that k = 2 and it doesn't test negative integers.
#include <GreatestProduct.h>
#include <gtest/gtest.h>
#include <MemLeakTest.h>
#include <StringUtility.h>
using TestParam = std::pair<std::pair<std::vector<int>, int>, int>;
class GreatestProductOfStep1Tests : public ::testing::TestWithParam<TestParam>, MemLeakTest {
};
TEST_P(GreatestProductOfStep1Tests, GreatestProductOfPositiveNumbersNfixedTo2ShouldReturnExpectedResult) {
const TestParam &p = GetParam();
int expected = p.second;
const std::vector<int> &numbers = p.first.first;
int N = p.first.second;
EXPECT_EQ(expected, GreatestProduct(numbers, N))
<< "Did call GreatestProductOf(" << utility::ToString<int>(numbers) << ", " << N << ")\n";
}
std::vector<TestParam> greatest_product_test_positive_n_eq_2_data{
{{{0, 1, 2, 3, 4}, 2}, 12},
{{{6, 6, 6}, 2}, 36},
{{{9, 8, 3, 5, 8, 1, 3, 5, 10}, 2}, 90},
{{{17, 5, 9, 1000, 15689, 57, 89, 10, 89, 283, 197, 0, 0, 132, 45, 78, 18, 15, 89,
19203, 98, 14, 78, 45, 35, 23, 24, 25, 46, 45, 756, 7567, 123, 890, 99, 98, 51,
991, 9123, 8912, 89534, 8923, 1823, 7385, 91, 1748, 1, 1, 893, 2813,
1381, 23, 563, 645, 24, 24, 51, 839, 38, 34, 35, 123, 324, 9283, 22, 19}, 2}, 1719321402},
{{{1, 1}, 2}, 1},
{{{0, 1}, 2}, 0},
{{{3789, 999}, 2}, 3785211}};
INSTANTIATE_TEST_CASE_P(GreatestProductOfStep1Tests,
GreatestProductOfStep1Tests,
::testing::ValuesIn(greatest_product_test_positive_n_eq_2_data));

Error initializing map

Okay so inside my ArcherArmor.cpp, I'm trying to figure out why the map initializer list isn't working, but for some reason I keep getting "Error C2593: 'operator =' is ambiguous". Here is my code:
I also have a class which ArcherArmor is derived from, that has a struct called Armor, which I left out for simplicity. The main problem is that error! The only thing I can think of is that im initializing the map wrong.
//ArcherArmor.h
#include <string>
#include <map>
class ArcherArmor
{
private:
map <int, Armor> soldier_armor;
public:
void ArcherArmor_shop();
};
//ArcherArmor.cpp
#include "ArcherArmor.h"
void ArcherArmor::ArcherArmor_shop(){
soldier_armor = {//name, damage, price
{1, Armor("Meito Ichimonji", 4, 150, 1)},
{2, Armor("Shusui", 10, 230, 2)},
{3, Armor("Apocalypse", 16, 300, 3)},
{4, Armor("Blade of Scars", 24, 550, 4)},
{5, Armor("Ragnarok", 32, 610, 5)},
{6, Armor("Eternal Darkness", 40, 690, 6)},
{7, Armor("Masamune", 52, 750, 7)},
{8, Armor("Soul Calibur", 60, 900, 8)}
};
}
//Main.cpp
/*code left our for simplicity*/
The part of your code given here seems to be OK. I used a tuple type to replace your Armor type (not shown in your question), and the code compiles fine with gcc (4.8.1). I think the problem is elsewhere in your code.
//ArcherArmor.h
#include <string>
#include <map>
#include <tuple>
using namespace std;
using Armor = std::tuple<string,int,int,int>;
class ArcherArmor
{
private:
map <int, Armor> soldier_armor;
public:
void ArcherArmor_shop();
};
//ArcherArmor.cpp
//#include "ArcherArmor.h"
void ArcherArmor::ArcherArmor_shop(){
soldier_armor = {//name, damage, price
{1, Armor("Meito Ichimonji", 4, 150, 1)},
{2, Armor("Shusui", 10, 230, 2)},
{3, Armor("Apocalypse", 16, 300, 3)},
{4, Armor("Blade of Scars", 24, 550, 4)},
{5, Armor("Ragnarok", 32, 610, 5)},
{6, Armor("Eternal Darkness", 40, 690, 6)},
{7, Armor("Masamune", 52, 750, 7)},
{8, Armor("Soul Calibur", 60, 900, 8)}
};
}
int main() {
ArcherArmor a;
return 0;
}

All pushed back values in vector changing to default value

In an effort to reduce multiple functions (that were nearly identical) in my code, I decided to consolidate them all into one function which takes an additional parameter (a class with multiple parameters, actually), and then uses those values to imitate what the multiple functions would have done. Then, long story short, I put each of those class declarations into a vector, and now my program seems dysfunctional.
My multiple instances of a class:
FragmentCostParameters commonFCP = FragmentCostParameters(.05, 0);
FragmentCostParameters rareFCP = FragmentCostParameters(.05, 50);
FragmentCostParameters uniqueFCP = FragmentCostParameters(.05, 125);
FragmentCostParameters legendaryFCP = FragmentCostParameters(.02, 175);
FragmentCostParameters crystallineFCP = FragmentCostParameters(.02, 250);
FragmentCostParameters superEliteFCP = FragmentCostParameters(.02, 300);
Which get placed into a vector by:
vector<FragmentCostParameters> FCPs(6);
FCPs.push_back(FragmentCostParameters(.05, 0));
FCPs.push_back(FragmentCostParameters(.05, 50));
FCPs.push_back(FragmentCostParameters(.05, 125));
FCPs.push_back(FragmentCostParameters(.02, 175));
FCPs.push_back(FragmentCostParameters(.02, 250));
FCPs.push_back(FragmentCostParameters(.02, 300));
Additionally, that class is defined below:
class FragmentCostParameters {
public:
double multFactor;
double subtractFactor;
FragmentCostParameters(double _multFactor, double _subtractFactor){
multFactor = _multFactor;
subtractFactor = _subtractFactor;
}
FragmentCostParameters(){
multFactor = .05;
subtractFactor = 0;
}
};
Now, you'll notice that the default constructor for the FragmentCostParameters involves setting multFactor = .05 and subtractFactor = 0. However, it seems that no matter what I push back, each of the values in my vector become mutated into those values. At least, that's what VS 2011 tells me the values are equal to when I'm looking at them in a local scope in the following function (which is the only place they're used).
int FragmentCost(double skillLevel, int duration, FragmentCostParameters FCP){
return max((int)(ceil(FCP.multFactor*(skillLevel-FCP.subtractFactor))*ceil(duration/30.0)) , 0);
}
And the only place that FragmentCost is called is from this function below, which is supposed to pass different values .. but somewhere in the process, when I look at locals in FragmentCost, they're always the default values in the constructor for the class.
//given a skill level + duration, will return an array with the fragment usage
int* regularTotalFragCost(int skillLevel, int duration){
int fragments[7] = {0,0,0,0,0,0,0};
fragments[0]+= FragmentCost(skillLevel,duration, FCPs[0]);
fragments[1]+= FragmentCost(skillLevel,duration, FCPs[0]);
fragments[0]+= FragmentCost(skillLevel,duration, FCPs[1]);
fragments[2]+= FragmentCost(skillLevel,duration, FCPs[1]);
fragments[0]+= FragmentCost(skillLevel,duration, FCPs[2]);
fragments[3]+= FragmentCost(skillLevel,duration, FCPs[2]);
fragments[0]+= FragmentCost(skillLevel,duration, FCPs[3]);
fragments[4]+= FragmentCost(skillLevel,duration, FCPs[3]);
fragments[0]+= FragmentCost(skillLevel,duration, FCPs[4]);
fragments[5]+= FragmentCost(skillLevel,duration, FCPs[4]);
fragments[0]+= FragmentCost(skillLevel,duration, FCPs[5]);
fragments[6]+= FragmentCost(skillLevel,duration, FCPs[5]);
return fragments;
}
For some reason I feel that I'm making a really stupid mistake somewhere, but for the life of me I can't seem to figure it out. I would appreciate any help and/or advice anyone could offer.
EDIT: Here's what the values for fragments[] in regularTotalFragCost are supposed to be if everything is working correctly, using a couple test values (skillLevel = 250 and duration = 30)
FCPs[0] : Fragments: 13, 13, 0, 0, 0, 0, 0,
FCPs[1] : Fragments: 17, 15, 2, 0, 0, 0, 0,
FCPs[2] : Fragments: 20, 14, 5, 1, 0, 0, 0,
FCPs[3] : Fragments: 29, 14, 9, 5, 1, 0, 0,
FCPs[4] : Fragments: 32, 13, 10, 7, 2, 0, 0,
FCPs[5] : Fragments: 32, 13, 10, 7, 2, 0, 0,
And here is what they are as of right now:
FCPs[0] : Fragments: 78, 13, 13, 13, 13, 13, 13,
FCPs[1] : Fragments: 78, 13, 13, 13, 13, 13, 13,
FCPs[2] : Fragments: 78, 13, 13, 13, 13, 13, 13,
FCPs[3] : Fragments: 78, 13, 13, 13, 13, 13, 13,
FCPs[4] : Fragments: 78, 13, 13, 13, 13, 13, 13,
FCPs[5] : Fragments: 78, 13, 13, 13, 13, 13, 13,
vector<FragmentCostParameters> FCPs(6);
creates a vector of 6 default-constructed values, numbered 0-5.
FCPs.push_back(FragmentCostParameters(.05, 0));
adds value at index 6
int fragments[7] is a local variable on the stack. Once that function returns, that memory is no longer valid. You are returning a pointer to that local variable and any access to that memory is undefined behavior.
Instead, return an std::array:
std::array<int, 7> fragments = {}; // value initialize
return fragments;
Hint: How big is your vector after you push_back your FCPs?