├── ChannelsCPP ├── IChannel.cpp ├── OChannel.cpp ├── ChannelBuffer.cpp ├── ChannelUtility.cpp ├── Circular_buffer.cpp ├── Channel.cpp ├── OChannel.h ├── ChannelsCPP.vcxproj.filters ├── Circular_buffer.h ├── ChannelBuffer.h ├── ChannelUtility.h ├── IChannel.h ├── Source.cpp ├── Channel.h └── ChannelsCPP.vcxproj ├── CMakeLists.txt ├── LICENSE ├── ChannelsCPP.sln ├── .gitattributes ├── .gitignore └── README.md /ChannelsCPP/IChannel.cpp: -------------------------------------------------------------------------------- 1 | #include "IChannel.h" 2 | -------------------------------------------------------------------------------- /ChannelsCPP/OChannel.cpp: -------------------------------------------------------------------------------- 1 | #include "OChannel.h" 2 | 3 | 4 | -------------------------------------------------------------------------------- /ChannelsCPP/ChannelBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "ChannelBuffer.h" 2 | 3 | -------------------------------------------------------------------------------- /ChannelsCPP/ChannelUtility.cpp: -------------------------------------------------------------------------------- 1 | #include "ChannelUtility.h" 2 | 3 | -------------------------------------------------------------------------------- /ChannelsCPP/Circular_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Circular_buffer.h" 2 | 3 | 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.5) 2 | 3 | set (CMAKE_CXX_STANDARD 14) 4 | set (CMAKE_EXE_LINKER_FLAGS "-fsanitize=address,undefined") 5 | add_executable(ChannelsTest ChannelsCPP/Source.cpp ChannelsCPP/Channel.cpp) 6 | target_link_libraries(ChannelsTest PUBLIC pthread) 7 | target_compile_options(ChannelsTest PRIVATE "-fsanitize=address,undefined") 8 | -------------------------------------------------------------------------------- /ChannelsCPP/Channel.cpp: -------------------------------------------------------------------------------- 1 | #include "Channel.h" 2 | using namespace go; 3 | 4 | 5 | /*template 6 | T chan::Channel::getNextValue() 7 | { 8 | return T(); 9 | } 10 | 11 | template 12 | chan::Channel::Channel() 13 | { 14 | } 15 | 16 | template 17 | chan::Channel::~Channel() 18 | { 19 | } 20 | */ 21 | 22 | template 23 | T& operator<<(Chan& ch, const T& obj) 24 | { 25 | ch.insertValue(obj); 26 | return ch; 27 | } 28 | 29 | 30 | template 31 | Chan& operator >> (Chan& ch, T& obj) 32 | { 33 | obj = ch.getNextValue(); 34 | return obj; 35 | 36 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Francis Lemaire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ChannelsCPP.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChannelsCPP", "ChannelsCPP\ChannelsCPP.vcxproj", "{85F0720E-320F-4216-B74C-DF2B218A2D4D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Debug|x64.ActiveCfg = Debug|x64 17 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Debug|x64.Build.0 = Debug|x64 18 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Debug|x86.ActiveCfg = Debug|Win32 19 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Debug|x86.Build.0 = Debug|Win32 20 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Release|x64.ActiveCfg = Release|x64 21 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Release|x64.Build.0 = Release|x64 22 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Release|x86.ActiveCfg = Release|Win32 23 | {85F0720E-320F-4216-B74C-DF2B218A2D4D}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /ChannelsCPP/OChannel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ChannelBuffer.h" 4 | 5 | namespace go 6 | { 7 | template class IChan; 8 | 9 | template 10 | class OChan //: private Chan 11 | { 12 | protected: 13 | std::shared_ptr> m_buffer; 14 | OChan(std::shared_ptr> buffer) : m_buffer(buffer) {} 15 | public: 16 | OChan() = default; 17 | OChan(const OChan& ch) = default;// :m_buffer(ch.m_buffer) {} 18 | OChan(OChan&& ch) { swap(m_buffer, ch.m_buffer); } 19 | //Insert in channel 20 | friend OChan& operator<<(OChan& ch, const T& obj) 21 | { 22 | ch.m_buffer->insertValue(obj); 23 | return ch; 24 | } 25 | friend OChan& operator >> (const T& obj, OChan& ch) 26 | { 27 | ch.m_buffer->insertValue(obj); 28 | return ch; 29 | 30 | } 31 | 32 | template 33 | friend OChan& operator<<(OChan& ch, const IChan& obj) 34 | { 35 | T temp; 36 | temp << obj; 37 | ch << temp; 38 | return ch; 39 | } 40 | 41 | template 42 | friend OChan& operator >> (const IChan& obj, OChan& ch) 43 | { 44 | T temp; 45 | temp << obj; 46 | ch << temp; 47 | return ch; 48 | 49 | } 50 | 51 | //Stream 52 | friend std::ostream& operator<<(std::ostream& os, OChan& ch) 53 | { 54 | os << ch.m_buffer->getNextValue(); 55 | return os; 56 | } 57 | 58 | void close() 59 | { 60 | m_buffer->close(); 61 | } 62 | }; 63 | 64 | } -------------------------------------------------------------------------------- /ChannelsCPP/ChannelsCPP.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Fichiers sources 20 | 21 | 22 | Fichiers sources 23 | 24 | 25 | Fichiers sources 26 | 27 | 28 | Fichiers sources 29 | 30 | 31 | Fichiers sources 32 | 33 | 34 | Fichiers sources 35 | 36 | 37 | Fichiers sources 38 | 39 | 40 | 41 | 42 | Fichiers d%27en-tête 43 | 44 | 45 | Fichiers d%27en-tête 46 | 47 | 48 | Fichiers d%27en-tête 49 | 50 | 51 | Fichiers d%27en-tête 52 | 53 | 54 | Fichiers d%27en-tête 55 | 56 | 57 | Fichiers d%27en-tête 58 | 59 | 60 | -------------------------------------------------------------------------------- /ChannelsCPP/Circular_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | namespace go 6 | { 7 | namespace internal 8 | { 9 | template 10 | class Circular_buffer 11 | { 12 | typedef T& reference; 13 | typedef const T& const_reference; 14 | typedef T* pointer; 15 | typedef const T* const_pointer; 16 | typedef decltype(Buffer_Size) syze_type; 17 | 18 | std::unique_ptr m_buffer; 19 | pointer m_front; 20 | pointer m_end; 21 | size_t m_size; 22 | 23 | void increment(pointer& p)const 24 | { 25 | if (p + 1 == &m_buffer[0] + Buffer_Size) 26 | { 27 | p = &m_buffer[0]; 28 | } 29 | else 30 | { 31 | ++p; 32 | } 33 | } 34 | public: 35 | Circular_buffer() 36 | :m_buffer(new T[Buffer_Size]), 37 | m_front(&m_buffer[0]), 38 | m_end(&m_buffer[0]), 39 | m_size(0) 40 | { 41 | 42 | } 43 | 44 | ~Circular_buffer() 45 | { 46 | m_front = nullptr; 47 | m_end = nullptr; 48 | 49 | } 50 | 51 | bool empty() const 52 | { 53 | return m_size <= 0; 54 | } 55 | 56 | bool full() const 57 | { 58 | return m_size == Buffer_Size; 59 | } 60 | 61 | T& front() 62 | { 63 | if (empty()) 64 | throw std::out_of_range("container is empty"); 65 | return *m_front; 66 | } 67 | 68 | const T& front() const 69 | { 70 | if (empty()) 71 | throw std::out_of_range("container is empty"); 72 | return *m_front; 73 | } 74 | 75 | const T& back() const 76 | { 77 | if (empty()) 78 | throw std::out_of_range("container is empty"); 79 | return *m_end; 80 | } 81 | 82 | syze_type size() const 83 | { 84 | return m_size; 85 | } 86 | 87 | void push_back(T item) 88 | { 89 | if (full()) 90 | throw std::out_of_range("container is full"); 91 | *m_end = item; 92 | increment(m_end); 93 | ++m_size; 94 | } 95 | 96 | template 97 | void emplace_back(Args && ... args) 98 | { 99 | push_back(T(std::forward(args)...)); 100 | } 101 | 102 | void pop() 103 | { 104 | if (empty()) 105 | throw std::out_of_range("container is empty"); 106 | --m_size; 107 | increment(m_front); 108 | } 109 | 110 | T pop_front() 111 | { 112 | if (empty()) 113 | throw std::out_of_range("container is empty"); 114 | --m_size; 115 | T temp = *m_front; 116 | increment(m_front); 117 | return temp; 118 | } 119 | 120 | void push(T item) 121 | { 122 | push_back(item); 123 | } 124 | 125 | 126 | }; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /ChannelsCPP/ChannelBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Circular_buffer.h" 11 | namespace go 12 | { 13 | namespace internal 14 | { 15 | template 16 | class ChannelBuffer 17 | { 18 | private: 19 | Circular_buffer buffer; 20 | std::mutex bufferLock; 21 | std::condition_variable inputWait; 22 | std::condition_variable outputWait; 23 | std::atomic_bool is_closed; 24 | public: 25 | ChannelBuffer():is_closed(false) { } 26 | ~ChannelBuffer() = default; 27 | 28 | //Wait for the next value 29 | T getNextValue() 30 | { 31 | std::unique_lock ulock(bufferLock); 32 | if (buffer.empty()) 33 | { 34 | if (is_closed) //if closed we always return the default initialisation of T 35 | return{}; 36 | inputWait.wait(ulock, [&]() {return !buffer.empty() || is_closed; }); 37 | if (buffer.empty() && is_closed) // when we close the channel and there was more waiting then available value 38 | return{}; 39 | } 40 | 41 | T temp; 42 | std::swap(temp, buffer.front()); 43 | buffer.pop(); 44 | outputWait.notify_one(); 45 | return temp; 46 | } 47 | 48 | //Should use std::optional but MSVC and Clang doesn't support it yet :( #C++17 49 | std::unique_ptr tryGetNextValue() 50 | { 51 | if (is_closed) //if closed we always return the default initialisation of T 52 | return std::make_unique(T{}); 53 | std::unique_lock ulock(bufferLock); 54 | if (buffer.empty()) 55 | { 56 | return nullptr; 57 | } 58 | std::unique_ptr temp = std::make_unique(buffer.front()); 59 | buffer.pop(); 60 | outputWait.notify_one(); 61 | return std::move(temp); 62 | } 63 | 64 | void insertValue(T in) 65 | { 66 | if (!is_closed) 67 | { 68 | { 69 | std::unique_lock lock(bufferLock); 70 | if (buffer.full()) 71 | { 72 | outputWait.wait(lock, [&]() {return !buffer.full() || is_closed; }); 73 | if (is_closed) // if channel was closed end all awaiting inputs (cannot send to a closed channel) 74 | { 75 | return; 76 | } 77 | } 78 | buffer.push(in); 79 | } 80 | inputWait.notify_one(); 81 | } 82 | } 83 | 84 | void close() 85 | { 86 | is_closed = true; 87 | inputWait.notify_one(); 88 | outputWait.notify_all(); 89 | 90 | } 91 | 92 | bool isClosed() 93 | { 94 | return is_closed; 95 | } 96 | 97 | }; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ChannelsCPP/ChannelUtility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //#include "Channel.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /*#include "IChannel.h" 10 | #include "OChannel.h"*/ 11 | 12 | namespace go 13 | { 14 | namespace internal 15 | { 16 | template constexpr bool dependent_false = false; 17 | } 18 | 19 | template class Chan; 20 | 21 | template class OChan; 22 | template class IChan; 23 | 24 | 25 | // Select statements references: https://golang.org/ref/spec#Select_statements 26 | class Case 27 | { 28 | std::function task; 29 | public: 30 | template 31 | Case(IChan ch, func f) 32 | { 33 | task = [=]() { 34 | auto val = ch.m_buffer->tryGetNextValue(); 35 | if (val) 36 | { 37 | f(*val); 38 | }; 39 | return val == nullptr; 40 | }; 41 | } 42 | 43 | template 44 | Case(OChan ch, func f) 45 | { 46 | task = [=]() { 47 | f(); 48 | return true; 49 | }; 50 | } 51 | 52 | template 53 | Case(Chan ch, func f) : Case(IChan(ch), std::forward(f)) {} 54 | 55 | Case(const Case&) = default; 56 | Case() { task = []() {return true; }; } 57 | 58 | bool operator() () 59 | { 60 | return task(); 61 | } 62 | }; 63 | 64 | class Default 65 | { 66 | std::function task; 67 | public: 68 | template 69 | Default(func f) 70 | { 71 | task = f; 72 | } 73 | 74 | void operator() () 75 | { 76 | task(); 77 | } 78 | }; 79 | 80 | class Select 81 | { 82 | std::vector cases; 83 | 84 | bool randomExec() 85 | { 86 | std::random_device rd; 87 | std::mt19937 g(rd()); 88 | std::shuffle(begin(cases), end(cases), g); 89 | for (auto& c : cases) 90 | { 91 | if (!c()) 92 | return true; 93 | } 94 | return false; 95 | } 96 | 97 | template 98 | void exec(Case && c, T &&... params) 99 | { 100 | cases.emplace_back(c); 101 | exec(std::forward(params)...); 102 | } 103 | 104 | void exec(Case && c) 105 | { 106 | cases.emplace_back(c); 107 | randomExec(); 108 | } 109 | 110 | void exec(Default && d) 111 | { 112 | if(!randomExec()) 113 | d(); 114 | } 115 | template 116 | void exec(Default && c, T &&... params) 117 | { 118 | static_assert(internal::dependent_false, "There should only be at most 1 Default case and it must be the last parameter of the Select"); 119 | } 120 | 121 | public: 122 | template 123 | Select(T &&... params)//:cases(sizeof...(params)) 124 | { 125 | cases.reserve(sizeof...(params)); 126 | exec(std::forward(params)...); 127 | } 128 | }; 129 | 130 | // Close references: https://golang.org/ref/spec#Close 131 | // we try to avoid exceptions so we will have custom implementation 132 | template 133 | void Close(OChan ch) 134 | { 135 | ch.close(); 136 | } 137 | 138 | template 139 | Chan && make_Chan() 140 | { 141 | return Chan(); 142 | } 143 | 144 | 145 | } 146 | 147 | -------------------------------------------------------------------------------- /ChannelsCPP/IChannel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "ChannelBuffer.h" 5 | namespace go 6 | { 7 | class Case; 8 | 9 | 10 | template class OChan; 11 | 12 | // Inspired by http://h-deb.clg.qc.ca/Sujets/Divers--cplusplus/Iterateurs-generateurs.html From Patrice Roy 13 | // References: https://golang.org/ref/spec#For_statements 14 | // To be improved... 15 | template 16 | class IChan_Iterator :public std::iterator //:public std::shared_ptr 17 | { 18 | std::shared_ptr> m_buffer; 19 | T val; 20 | 21 | public: 22 | IChan_Iterator(std::shared_ptr> buffer, bool isEnd = false) :m_buffer(buffer) 23 | { 24 | if (!isEnd) 25 | operator++(); 26 | } 27 | IChan_Iterator(const IChan_Iterator&) = default; 28 | T& operator*() 29 | { 30 | return val; 31 | } 32 | IChan_Iterator& operator++() 33 | { 34 | //static_cast>(*this) = std::make_shared(m_buffer->getNextValue()); 35 | val = m_buffer->getNextValue(); 36 | return *this; 37 | } 38 | IChan_Iterator operator++(int) 39 | { 40 | IChan_Iterator tmp(*this); // copy 41 | operator++(); // pre-increment 42 | return tmp; // return old value 43 | } 44 | inline bool operator==(const IChan_Iterator& rhs) const { return m_buffer->isClosed(); } 45 | inline bool operator!=(const IChan_Iterator& rhs) const { return !operator==(rhs); } 46 | 47 | }; 48 | template 49 | class IChan //: private Chan 50 | { 51 | protected: 52 | std::shared_ptr> m_buffer; 53 | IChan(std::shared_ptr> buffer) : m_buffer(buffer) {} 54 | public: 55 | IChan() = default; 56 | IChan(const IChan& ch) = default;//:m_buffer(ch.m_buffer) {} 57 | IChan(IChan&& ch) {swap(m_buffer,ch.m_buffer);}; 58 | 59 | //Extract from channel 60 | friend IChan& operator >> (IChan& ch, T& obj) 61 | { 62 | obj = ch.m_buffer->getNextValue(); 63 | return ch; 64 | 65 | } 66 | friend IChan& operator<<(T& obj, IChan& ch) 67 | { 68 | obj = ch.m_buffer->getNextValue(); 69 | return ch; 70 | } 71 | 72 | template 73 | friend IChan& operator >> (IChan& ch, OChan& obj) 74 | { 75 | T temp; 76 | ch >> temp; 77 | obj << temp; 78 | return ch; 79 | 80 | } 81 | template 82 | friend IChan& operator<<(OChan& obj, IChan& ch) 83 | { 84 | T temp; 85 | ch >> temp; 86 | obj << temp; 87 | return ch; 88 | } 89 | //Stream 90 | friend std::istream& operator >> (std::istream& is, IChan& ch) 91 | { 92 | T temp; 93 | is >> temp; 94 | ch << temp; 95 | return is; 96 | } 97 | 98 | 99 | using IChan_EndIterator = IChan_Iterator; 100 | 101 | IChan_Iterator begin() 102 | { 103 | return IChan_Iterator{ m_buffer }; 104 | } 105 | IChan_EndIterator end() 106 | { 107 | return{ m_buffer , true }; 108 | } 109 | 110 | friend class go::Case; 111 | 112 | }; 113 | 114 | } 115 | 116 | -------------------------------------------------------------------------------- /ChannelsCPP/Source.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Channel.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Circular_buffer.h" 9 | 10 | using namespace std; 11 | using namespace go; 12 | using namespace go::internal; 13 | 14 | template 15 | void fibonacci(Chan& c, Chan& quit) 16 | { 17 | int x=0, y = 1; 18 | for (bool go = true; go;) 19 | { 20 | Select 21 | { 22 | Case{c << x,[&]() 23 | { 24 | int t = x; 25 | x = y; 26 | y += t; 27 | }}, 28 | Case{quit,[&](auto v) 29 | { 30 | cout << "quit" << endl; 31 | go = false; 32 | }} 33 | }; 34 | } 35 | } 36 | template 37 | void f(T1& ch, T2 x, int time) 38 | { 39 | this_thread::sleep_for(chrono::seconds(time)); 40 | ch << x; 41 | } 42 | int main() 43 | { 44 | /*Circular_buffer buff; 45 | // Try on empty 46 | try 47 | { 48 | cout << buff.pop_front() << endl; 49 | } 50 | catch (const std::exception& e) 51 | { 52 | cout << e.what() << endl; 53 | } 54 | //testz 55 | try 56 | { 57 | buff.emplace_back(5); 58 | } 59 | catch (const std::exception& e) 60 | { 61 | cout << e.what() << endl; 62 | } 63 | // Test Pop 64 | try 65 | { 66 | cout << buff.pop_front() << endl; 67 | } 68 | catch (const std::exception& e) 69 | { 70 | cout << e.what() << endl; 71 | } 72 | //insert 73 | try 74 | { 75 | buff.emplace_back(5); 76 | } 77 | catch (const std::exception& e) 78 | { 79 | cout << e.what() << endl; 80 | } 81 | //full 82 | try 83 | { 84 | buff.emplace_back(5); 85 | } 86 | catch (const std::exception& e) 87 | { 88 | cout << e.what() << endl; 89 | } 90 | //exception full 91 | try 92 | { 93 | buff.emplace_back(5); 94 | } 95 | catch (const std::exception& e) 96 | { 97 | cout << e.what() << endl; 98 | }*/ 99 | 100 | 101 | { 102 | Chan c; 103 | thread([=]() mutable 104 | { 105 | for (size_t i = 0; i < 10; i++) 106 | { 107 | cout << c << endl; 108 | } 109 | }).detach(); 110 | 111 | for (size_t i = 0; i < 10; i++) 112 | { 113 | c << i; 114 | } 115 | this_thread::sleep_for(chrono::milliseconds(1)); // wait for last entry to be printed 116 | } 117 | 118 | cout << "------Demo fibonacci (https://tour.golang.org/concurrency/5)-----" << endl; 119 | 120 | Chan c; 121 | Chan quit; 122 | 123 | thread([&]() 124 | { 125 | for (size_t i = 0; i < 10; i++) 126 | { 127 | cout << c << endl; 128 | } 129 | quit << 0; 130 | }).detach(); 131 | fibonacci(c, quit); 132 | 133 | cout << "------Demo with Default-----" << endl; 134 | 135 | Chan ch; 136 | Chan ch2; 137 | 138 | auto t1 = thread(f, ref(ch), 1, 3); 139 | auto t2 = thread(f, ref(ch2), "J'aime",1 ); 140 | 141 | for (bool asd = true; asd;) 142 | { 143 | Select 144 | { 145 | Case{ch,[&](int x) 146 | { 147 | cout << "mon prof !!!" << endl << flush; 148 | cout << "Valeur recu: " << x << endl << flush; 149 | asd = false; 150 | }}, 151 | Case{ch2,[](string x) 152 | { 153 | cout << x << endl << flush; 154 | }}, 155 | Default{[]() 156 | { 157 | cout << "Waiting...." << endl< ch3; 170 | 171 | thread t3 = thread([&]() 172 | { 173 | for (int i = 0; i < 10; i++) 174 | { 175 | ch3 << i; 176 | this_thread::sleep_for(chrono::milliseconds(500)); 177 | } 178 | Close(ch3); 179 | }); 180 | 181 | for (auto &asd : ch3) 182 | { 183 | cout << asd << endl; 184 | } 185 | cout << "Fini" << endl; 186 | t3.join(); 187 | 188 | cout << "------Test buffered Channel-----" << endl; 189 | Chan multi; 190 | thread([&]() { 191 | multi << 1 << 2 << 3 << 4 << 5; 192 | this_thread::sleep_for(chrono::milliseconds(500)); 193 | Close(multi); 194 | }).detach(); 195 | for(auto& asd:multi) 196 | { 197 | cout << asd << endl; 198 | } 199 | 200 | //must be fix 201 | // This test doesn't make sens it's dead locking itself because output channel can only buffer 1 value and we end up trying to put 9 values in it 202 | /*cout << "------Test closing Channel-----" << endl; 203 | Chan tested; 204 | Chan output; 205 | vector vt; 206 | for (size_t i = 0; i < 10; i++) 207 | { 208 | vt.emplace_back([&]() 209 | { 210 | output << tested; 211 | }); 212 | } 213 | this_thread::sleep_for(chrono::seconds(1)); 214 | tested << 3 << 2 << 1 << 0 << -1 << -2; //doesn't work... find why (atomic not initialized) 215 | -5 >> (-4 >> (-3 >> tested)); 216 | //Close(tested); 217 | int i = 0; 218 | for (auto &asd : output) 219 | { 220 | ++i; 221 | cout << output << endl; 222 | } 223 | cout << "i: " << i << endl; 224 | for (auto &t : vt) 225 | { 226 | t.join(); 227 | }*/ 228 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /ChannelsCPP/Channel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ChannelBuffer.h" 11 | #include "IChannel.h" 12 | #include "OChannel.h" 13 | #include "ChannelUtility.h" 14 | #include "Circular_buffer.h" 15 | 16 | namespace go 17 | { 18 | //Channel Type references: https://golang.org/ref/spec#Channel_types 19 | template 20 | class Chan :public IChan, public OChan 21 | { 22 | public: 23 | Chan() 24 | { 25 | Chan::IChan::m_buffer = Chan::OChan::m_buffer = std::make_shared>(); 26 | //IChan::m_buffer = OChan::m_buffer = std::make_shared>(); 27 | } 28 | ~Chan() = default; 29 | 30 | //Insert in channel 31 | friend OChan& operator<<(Chan& ch, const T& obj) 32 | { 33 | return static_cast&>(ch) << obj; 34 | /*ch.m_buffer->insertValue(obj); 35 | return ch;*/ 36 | } 37 | friend OChan& operator >> (const T& obj, Chan& ch) 38 | { 39 | return static_cast&>(ch) << obj; 40 | 41 | /*ch.m_buffer->insertValue(obj); 42 | return ch;*/ 43 | 44 | } 45 | //Stream 46 | friend std::ostream& operator<<(std::ostream& os, Chan& ch) 47 | { 48 | return os << static_cast&>(ch); 49 | } 50 | friend std::istream& operator >> (std::istream& is, Chan& ch) 51 | { 52 | return is >> static_cast&>(ch); 53 | } 54 | 55 | 56 | 57 | }; 58 | 59 | /*template 60 | class Chan :public IChan, public OChan 61 | { 62 | public: 63 | Chan() 64 | { 65 | IChan::m_buffer = OChan::m_buffer = std::make_shared>(); 66 | } 67 | ~Chan() = default; 68 | 69 | //Insert in channel 70 | friend OChan operator<<(Chan& ch, const T& obj) 71 | { 72 | return static_cast>(ch) << obj; 73 | } 74 | friend OChan operator >> (const T& obj, Chan& ch) 75 | { 76 | return static_cast>(ch) << obj; 77 | 78 | } 79 | //Stream 80 | friend std::ostream& operator<<(std::ostream& os, Chan& ch) 81 | { 82 | return os << static_cast>(ch); 83 | } 84 | friend std::istream& operator >> (std::istream& is, Chan& ch) 85 | { 86 | return is >> static_cast>(ch); 87 | } 88 | 89 | 90 | 91 | };*/ 92 | 93 | } 94 | 95 | //template 96 | //using inf_chan = Chan::infinity(), std::queue>; 97 | 98 | //Specialized (Buffered) 99 | /*template 100 | class Channel 101 | { 102 | private: 103 | class ChannelBuffer 104 | { 105 | private: 106 | std::deque buffer; 107 | std::mutex bufferLock; 108 | std::condition_variable inputWait; 109 | public: 110 | ChannelBuffer() = default; 111 | ~ChannelBuffer() = default; 112 | T getNextValue() 113 | { 114 | std::unique_lock ulock(bufferLock); 115 | if (buffer.empty()) 116 | { 117 | inputWait.wait(ulock, [&]() {return !buffer.empty(); }); 118 | } 119 | T temp; 120 | std::swap(temp, buffer.front()); 121 | buffer.pop_front(); 122 | return temp; 123 | } 124 | void insertValue(T in) 125 | { 126 | { 127 | std::lock_guard lock(bufferLock); 128 | if (buffer.size() >= bufferSize) 129 | throw exception("Buffer full"); 130 | buffer.push_back(in); 131 | } 132 | inputWait.notify_one(); 133 | } 134 | 135 | }; 136 | std::shared_ptr m_channel; 137 | T getNextValue() 138 | { 139 | return m_channel->getNextValue(); 140 | } 141 | 142 | void insertValue(T val) 143 | { 144 | m_channel->insertValue(val); 145 | } 146 | 147 | public: 148 | Channel() 149 | { 150 | m_channel = std::make_shared(); 151 | } 152 | ~Channel() = default; 153 | 154 | //Extract from channel 155 | friend T& operator >> (Channel& ch, T& obj) 156 | { 157 | obj = ch.getNextValue(); 158 | return obj; 159 | 160 | } 161 | friend T& operator<<(T& obj, Channel& ch) 162 | { 163 | obj = ch.getNextValue(); 164 | return obj; 165 | } 166 | 167 | //Insert in channel 168 | friend Channel& operator<<(Channel& ch, const T& obj) 169 | { 170 | ch.insertValue(obj); 171 | return ch; 172 | } 173 | friend Channel& operator >> (const T& obj, Channel& ch) 174 | { 175 | ch.insertValue(obj); 176 | return ch; 177 | 178 | } 179 | 180 | //Stream 181 | friend std::ostream& operator<<(std::ostream& os, Channel& ch) 182 | { 183 | os << ch.getNextValue(); 184 | return os; 185 | } 186 | friend std::istream& operator >> (std::istream& is, Channel& ch) 187 | { 188 | T temp; 189 | is >> temp; 190 | ch << temp; 191 | return is; 192 | } 193 | 194 | 195 | };*/ 196 | 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChannelsCPP 2 | C++ implementation of the Go (GoLang) Channel and Select structure in a modern C++ manner. 3 | 4 | You will need a C++ compiler that has at least support for C++11 and part of C++14 (it might work on C++11 only but i didn't test). 5 | I have already identified parts of the code that could use C++17 features, but some feature are not yet implemented in my compiler. 6 | 7 | I also want to stabilize the code a bit more, do performance testing/ C++ version testing and organise the repo (when i do C++17 integration make a branch for each C++ version that i can support down to C++11 ... if it has all the features ... still not tested) 8 | 9 | In the future i will try to bring each implementation closer to the Go implementation by respecting the API design and behavior. 10 | ## Features list 11 | - [x] [Basic Go Channel type](#basic-channel) (will be modify when the [Buffered Channel](#buffered-channel) is implemented) 12 | - [x] [Buffered Go Channel type](#buffered-channel) 13 | - [x] Channel stream input/output operator overload for inserting and extracting 14 | - [x] [Select statement](#select) 15 | - [x] [Select statement](#select) with channel insertion 16 | - [x] [Select statement](#select) random case selection when multiple available 17 | - [ ] Custom For class, for cleaner use with select (no bool value) 18 | - [x] Range for loop on channel 19 | - [x] Close function (ties in with the Range for loop feature) 20 | - [ ] make_Chan function to replace the [make](https://golang.org/ref/spec#Making_slices_maps_and_channels) function in Go 21 | 22 | ## Features 23 | ### Channels 24 | Channel are simple and intuitive to use and interact like the [Go channel type](https://tour.golang.org/concurrency/2) 25 | Ther are two types of channel in go, the [basic channel type](https://tour.golang.org/concurrency/2) and the [buffered channel type](https://tour.golang.org/concurrency/3) 26 | > Channels are a typed conduit through which you can send and receive values. 27 | >-Go 28 | 29 | * Multi directional inserting operators overload (The data flows in the direction of the insertion/extraction operator.) 30 | * Multi directional extration operators overload (The data flows in the direction of the insertion/extraction operator.) 31 | * Basic stream operator overload 32 | * Channel to Channel inserting and extration 33 | * Chaining insertion and extraction 34 | * Iterators to work with C++ Range for loop (emulate the Go feature) 35 | 36 | #### Unbuffered Channel 37 | Unbuffered Channel only have 1 slot for data, so it's not exactly Unbuffered and as such does not behave like the Unbuffered Channel in Go that block until there's a sender and a receiver ready at the same time. (It will change in the future) 38 | 39 | ```C++ 40 | Chan ch; 41 | // inserting 42 | int i = 2; 43 | ch << 1; 44 | i >> ch; 45 | // extracting 46 | i << ch; 47 | ch >> y 48 | ``` 49 | #### Buffered Channel 50 | Buffered Channel are a type of channel that doesn't block unless the buffer is full. 51 | 52 | ```C++ 53 | Chan multi; 54 | 55 | thread([&]() { 56 | multi << 1 << 2 << 3 << 4 << 5; 57 | this_thread::sleep_for(chrono::milliseconds(500)); // Give some time to process the data 58 | Close(multi); 59 | }).detach(); 60 | 61 | for(auto& asd:multi) 62 | { 63 | cout << asd << endl; 64 | } 65 | ``` 66 | 67 | ### Select 68 | The C++ implementation of the Go Select statement 69 | 70 | * can have one or infinite [Case](#case) block (infinite = until you connot compile it) 71 | * can have zero or one [Default](#default) block (enforced in code) 72 | * support input in channel in [Case](#case) block channel evaluation (feature seems to be working but might need a bit more attention) 73 | * random channel ([Case](#case)) selection is now supported 74 | 75 | Go exemple (from [here](https://tour.golang.org/concurrency/5)) : 76 | ```Go 77 | func fibonacci(c, quit chan int) { 78 | x, y := 0, 1 79 | for { 80 | select { 81 | case c <- x: 82 | x, y = y, x+y 83 | case <-quit: 84 | fmt.Println("quit") 85 | return 86 | } 87 | } 88 | } 89 | 90 | func main() { 91 | c := make(chan int) 92 | quit := make(chan int) 93 | go func() { 94 | for i := 0; i < 10; i++ { 95 | fmt.Println(<-c) 96 | } 97 | quit <- 0 98 | }() 99 | fibonacci(c, quit) 100 | } 101 | 102 | ``` 103 | 104 | C++ exemple implementation : 105 | ```C++ 106 | void fibonacci(Chan& c, Chan& quit) 107 | { 108 | int x=0, y = 1; 109 | for (bool go = true; go;) 110 | { 111 | Select 112 | { 113 | Case{c << x,[&]() 114 | { 115 | int t = x; 116 | x = y; 117 | y += t; 118 | }}, 119 | Case{quit,[&](auto v) 120 | { 121 | cout << "quit" << endl; 122 | go = false; 123 | }} 124 | }; 125 | } 126 | } 127 | 128 | int main() 129 | { 130 | Chan c; 131 | Chan quit; 132 | 133 | thread([&]() 134 | { 135 | for (size_t i = 0; i < 10; i++) 136 | { 137 | cout << c << endl; 138 | } 139 | quit << 0; 140 | }).detach(); 141 | fibonacci(c, quit); 142 | } 143 | ``` 144 | 145 | #### Case 146 | Contains code to be executed when the channel that it's checking receive something. 147 | It support sending to the channel it's checking but the feature might need to be rework because it doesn't behave like the Go implementation. 148 | 149 | #### Default 150 | Execute it's code when no [Case](#case) block had a channel that received data. 151 | There can only be at most **1** Default block in a [Select](#select) statement and it must be the last block (this is enforced in the code). 152 | 153 | ### Close 154 | Function called on a [Channel](#channels) to close the stream. 155 | Most of the time it's use to notify a [Channel](#channels) that is being iterated over in a Range for loop. It is in fact the only way to exit a Range for loop. 156 | 157 | ### make_Chan 158 | Is included, but doesn't work! **DO NOT USE** 159 | -------------------------------------------------------------------------------- /ChannelsCPP/ChannelsCPP.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {85F0720E-320F-4216-B74C-DF2B218A2D4D} 23 | Win32Proj 24 | ChannelsCPP 25 | 8.1 26 | 27 | 28 | 29 | Application 30 | true 31 | v140 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v140 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v140 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v140 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | 100 | 101 | Level3 102 | Disabled 103 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | 105 | 106 | Console 107 | true 108 | 109 | 110 | 111 | 112 | Level3 113 | 114 | 115 | MaxSpeed 116 | true 117 | true 118 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 119 | 120 | 121 | Console 122 | true 123 | true 124 | true 125 | 126 | 127 | 128 | 129 | Level3 130 | 131 | 132 | MaxSpeed 133 | true 134 | true 135 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 136 | 137 | 138 | Console 139 | true 140 | true 141 | true 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | --------------------------------------------------------------------------------