STAR++ v0.2.2
C++ implementation of STAR voting
Loading...
Searching...
No Matches
STAR++

STAR++ is a C++ library that implements the STAR voting system. It allows one to setup single or multi-seat (bloc) elections and then determine the winners while following a set of several configurable rules. Additionally, it provides several mechanisms through which to glean additional information about the calculation process, such as the order that candidates were seeded into the runoff.

This project also presents a reference dataset format and application that demonstrate usage of the library, as well as aid in testing and conformance assurance. Some aspects of the reference format are covered in this documentation, but for more information, and information regarding the application, see the project's GitHub page.

See Also

Requirements

  • An x64, C++20 capable compiler
  • Qt6
  • CMake 3.23.0 or greater
  • OS
    • Windows 10 or above
    • Linux (untested on more general Unix systems)

Packaging

STAR++ is provided as a CMake package composed of a single library, several public header files, and the reference application.

Package Components:

  • Base - The main library
  • Frontend - The reference application
# Example STAR++ Import
find_package(STARpp)

If no components are specified in the find_package() call, only the main library will be imported.

STAR++ is also composed to gracefully support CMake's FetchContent mechanism. All exportable targets are named to match their corresponding component when packaged, and feature alias targets with the same names when building. This allows consumers to access the targets via the same name regardless of whether they are using a pre-built package of STAR++ or building it as a sub-project.

For example, the Base component corresponds to the starpp_base target, which can also be referred to via STARpp::Base.

Getting Started

Note
For a recommended alternative, see Source Build as a Direct CMake Dependency

1) Download the latest Release

2) Place the package somewhere CMake can find it

# Add to a default search path or...
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} path\to\starpp_package)

3) Import the package

find_package(STARpp 0.1) # Or whichever version

4) Link to the library

target_link_libraries(example_app PUBLIC STARpp::Base)

5) Include the desired public headers in your code

#include <star/election.h>
#include <star/calculator.h>

6) Review the rest of the documentation. Good places to start are the Election and Calculator classes.

Minimal Example

#include <star/election.h>
#include <star/calculator.h>
#include <iostream>
int main()
{
// Setup ballots
Star::Election::Voter jim{.name = "Jim", .anonymousName = "Voter 1"};
QList<Star::Election::Vote> jimVotes{
{.candidate = "Candidate 1", .score = 3},
{.candidate = "Candidate 2", .score = 1},
{.candidate = "Candidate 3", .score = 5}
};
Star::Election::Voter sarah{.name = "Sarah", .anonymousName = "Voter 2"};
QList<Star::Election::Vote> sarahVotes{
{.candidate = "Candidate 1", .score = 4},
{.candidate = "Candidate 2", .score = 0},
{.candidate = "Candidate 3", .score = 2}
};
Star::Election::Voter ted{.name = "Ted", .anonymousName = "Voter 3"};
QList<Star::Election::Vote> tedVotes{
{.candidate = "Candidate 1", .score = 3},
{.candidate = "Candidate 2", .score = 2},
{.candidate = "Candidate 3", .score = 1}
};
// Setup election
Star::Election::Builder eb("My Election");
eb.wBallot(ted, tedVotes)
.wBallot(sarah, sarahVotes)
.wBallot(ted, tedVotes);
eb.wSeatCount(3);
Star::Election election = eb.build();
assert(election.isValid());
// Setup Calculator
Star::Calculator calculator(&election);
// If you want calculation details...
QObject::connect(&calculator, &Star::Calculator::calculationDetail, [](const QString& detail){
std::cout << detail.toStdString() << std::endl;
});
// Determine outcome
Star::ElectionResult result = calculator.calculateResult();
assert(!result.isNull());
std::cout << "Winner(s): " << result.winners().join(", ").toStdString() << std::endl;
return 0;
}
The Calculator class provides the means to determine the result of an election.
Definition calculator.h:23
void calculationDetail(const QString &detail) const
The Election::Builder class is a utility through which elections can be prepared.
Definition election.h:85
The Election class represents a collection of candidates and ballots.
Definition election.h:19
bool isValid() const
Definition election.cpp:43
The ElectionResult class holds the outcome of an election.
Definition electionresult.h:15
The Election::Voter struct contains the details of a voter participating in an election.
Definition election.h:53
QString name
Definition election.h:54

Building From Source

The latest generally stable source is available in the 'master' branch of https://github.com/oblivioncth/STARpp, while the most up-to-date source can be found in the 'dev' branch.

The requirements for building from Git are the same as for using STAR++, with the obvious exception that Doxygen (as well as Graphviz) is also needed to build the documentation.

If newer to working with Qt, it is easiest to build from within Qt creator as it handles a large portion of environment setup, including adding Qt to CMake's package search list, automatically. Simply make sure that a kit is configured in Qt Creator that uses a compatible version of Qt, open the CMakeLists.txt file as a project, and build with the desired configuration.

Alternatively, you can use the qt-cmake wrapper for similar Qt environment automation when building the project on the command-line, which is shown further down.

The CMake project is designed to be used with multi-configuration generators such as Visual Studio or Ninja Multi-Config (recommended), and may require some tweaking to work with single configuration generators.

CMake Options:

  • STARPP_DOCS - Set to ON in order to generate the documentation target (OFF)
  • STARPP_TESTS - Set to ON in order to generate the test targets (OFF)
  • BUILD_SHARED_LIBS - Build STAR++ as a shared library instead of a static one (OFF)

CMake Targets:

  • all - Builds the STAR++ library and reference application
  • install - Installs the build output into CMAKE_INSTALL_PREFIX
  • starpp_base - Builds the STAR++ library
  • starpp_frontend - Builds the STAR++ reference application
  • starpp_docs - Builds the STAR++ documentation
  • starpp_tst_... - Builds the various test targets. To actually run tests, just build the general CMake tests target test.

CMake Install Components:

  • starpp - Installs top-level files (README.md, CMake package configuration files, etc.)
  • starpp_base - Installs the built library
  • starpp_frontend - Installs the built reference application
  • starpp_docs - Installs the built documentation

If STAR++ is configured as a sub-project, its install components are automatically removed from the all component, as to not pollute the install directory of the top-level project. They can still be installed by directly referencing their component names as shown above.

Documentation:

In order for the starpp_docs target to be generated the CMake cache variable STARPP_DOCS must be set to ON when CMake is invoked:

cmake.exe (...) -D STARPP_DOCS=ON

The STAR++ documentation supports two optional, but highly recommended features:

  • Linking to Qt documentation
  • Generating a Qt Compressed Help (.qch) file for use in Qt Creator

In order to enable these features, the CMake variables QT_DOCS_DIR and QT_HELP_GEN_PATH respectively must be available. STAR++ tries to set them automatically according to the following defaults, but these can be overridden by passing definitions for the variables to CMake on the command line via -D.

# Optional documentation defaults
# Here <QT_ROOT> refers to the install directory of a given Qt build
QT_DOCS_DIR: <QT_ROOT>/doc
(Windows) QT_HELP_GEN_PATH: <QT_ROOT>/bin/qhelpgenerator.exe
(Linux) QT_HELP_GEN_PATH: <QT_ROOT>/libexec/qhelpgenerator

If supplying QT_DOCS_DIR manually, it must be set to the root path that contains documentation for the Qt version you are building with. It should look something like this:

doc/
├── config
├── global
├── qdoc
├── qmake
├── qtcmake
├── qtconcurrent
├── qtcore
├── ...
├── qdoc.qch
├── qmake.qch
└── ...

# In this case QT_DOCS_DIR should point to the directory 'doc'.

The path for this documentation varies depending on how you obtained Qt, but is generally placed in one of two locations:

# Official Qt Builds from Maintenance Tool/Installer
<QT_SOFTWARE>/Docs/Qt-<QT_VERSION>

# Self-built Qt
<QT_ROOT>/doc

# NOTE:
# By default on Windows <QT_SOFTWARE> is C:\Program Files\Qt
# On Linux it is often /usr/local/Qt

Tests:

The project contains a suite of tests to ensure that the library functions as intended. They will be expanded upon as the library matures.

Package:

By default, the CMakeLists project configures CPack to create an artifact ZIP containing:

  • The library
  • The reference application
  • Documentation

The following is the general build process required to successfully generate this package via a shadow build on Windows. Adjust the configuration as you see fit:

# Set the environment variables that follow as desired
# Setup C++ Build Environment
CALL "%VS_PATH%\Common7\Tools\VsDevCmd.bat" -arch=amd64
# Configure CMake using Qt wrapper
CALL "%Qt_ROOT%\bin\qt-cmake" -G "Ninja Multi-Config" -S "%STARPP_SOURCE_DIR%" -B "%STARPP_BUILD_DIR%" -D STARPP_DOCS=ON
# Go to Build directory
cd /D "%STARPP_BUILD_DIR%"
# Build the Debug/Release library, reference app, and documentation
cmake.exe --build . --target all --config Debug
cmake.exe --build . --target all --config Release
# Install Debug/Release libraries, reference application, and documentation
cmake --install . --config Debug
cmake --install . --config Release
# Create the output package
cpack.exe -C Debug;Release

Source Build as a Direct CMake Dependency

If you want to use STAR++ compiled from source directly as a dependency in your CMake project and don't care about the intermediary redistributables, it is recommended to do the following.

Create 'FetchSTARpp.cmake' and add it to CMAKE_MODULE_PATH:

# FetchSTARpp.cmake - REQUIRES GIT
# This will checkout STAR++, make its targets available directly at configure time without needing to call
# find_package(), and automatically build it as a dependency at build time.
function(fetch_starpp git_ref)
include(FetchContent)
FetchContent_Declare(STARpp
GIT_REPOSITORY "https://github.com/oblivioncth/STARpp"
GIT_TAG ${git_ref}
)
FetchContent_MakeAvailable(STARpp)
endfunction()

Then in your CMake project:

include(FetchSTARpp)
fetch_starpp(<commitish_here>) # Get STAR++
add_executable(SomeExe
...
)
target_link_libraries(SomeExe
PRIVATE
STARpp::Base
)

This allows for more flexibility in downstream projects as they can more easily alter the configuration of STAR++ on-the-fly as needed.